Merge branch '6.2.0-pubcontent' into 7.0.0-pubcontent

Conflicts:
	src/Umbraco.Core/Cache/CacheProviderBase.cs
	src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs
	src/Umbraco.Core/Cache/NullCacheProvider.cs
	src/Umbraco.Core/Cache/StaticCacheProvider.cs
	src/Umbraco.Core/Configuration/UmbracoSettings.cs
	src/Umbraco.Core/CoreBootManager.cs
	src/Umbraco.Core/Dynamics/PropertyResult.cs
	src/Umbraco.Core/Models/IPublishedContentProperty.cs
	src/Umbraco.Core/Models/PublishedItemType.cs
	src/Umbraco.Core/PropertyEditors/IPropertyEditorValueConverter.cs
	src/Umbraco.Core/PropertyEditors/PropertyEditorValueConvertersResolver.cs
	src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs
	src/Umbraco.Core/PublishedContentExtensions.cs
	src/Umbraco.Core/PublishedContentHelper.cs
	src/Umbraco.Core/Umbraco.Core.csproj
	src/Umbraco.Tests/CodeFirst/StronglyTypedMapperTest.cs
	src/Umbraco.Tests/LibraryTests.cs
	src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs
	src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
	src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
	src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs
	src/Umbraco.Web/ExamineExtensions.cs
	src/Umbraco.Web/Models/DynamicPublishedContent.cs
	src/Umbraco.Web/Models/XmlPublishedContent.cs
	src/Umbraco.Web/Models/XmlPublishedContentProperty.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
	src/Umbraco.Web/PublishedContentExtensions.cs
	src/Umbraco.Web/Templates/TemplateUtilities.cs
	src/Umbraco.Web/Umbraco.Web.csproj
	src/Umbraco.Web/WebBootManager.cs
	src/Umbraco.Web/umbraco.presentation/macro.cs
	src/umbraco.MacroEngines/RazorDynamicNode/PropertyResult.cs
	src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs
This commit is contained in:
Stephan
2013-09-23 21:57:22 +02:00
138 changed files with 10417 additions and 6251 deletions
+1 -1
View File
@@ -100,7 +100,7 @@ namespace Umbraco.Core
//
public bool IsConfigured
{
// fixme - we should not do this - ok for now
// todo - we should not do this - ok for now
get
{
return Configured;
-1
View File
@@ -47,6 +47,5 @@ namespace Umbraco.Core.Cache
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
}
}
+8
View File
@@ -74,6 +74,14 @@ namespace Umbraco.Core
{
}
internal void ClearStaticCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
if (_enableCache)
_staticCache.ClearCacheObjectTypes(predicate);
else
_nullStaticCache.ClearCacheObjectTypes(predicate);
}
/// <summary>
/// Private ctor used for creating a disabled cache helper
/// </summary>
+11 -3
View File
@@ -7,6 +7,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
@@ -17,6 +18,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Profiling;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Publishing;
using Umbraco.Core.Macros;
using Umbraco.Core.Services;
@@ -308,13 +310,16 @@ namespace Umbraco.Core
//the database migration objects
MigrationResolver.Current = new MigrationResolver(
() => PluginManager.Current.ResolveMigrationTypes());
//NOTE: These are legacy in v7+ and will eventually need to be removed.
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
// todo: remove once we drop IPropertyEditorValueConverter support.
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
PluginManager.Current.ResolvePropertyEditorValueConverters());
//add the internal ones, these are not public currently so need to add them manually
PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
PluginManager.Current.ResolvePropertyValueConverters());
// fixme - why not use the following syntax?
//PluginManager.Current.ResolveTypes<IPropertyValueConverter>());
// this is how we'd switch over to DefaultShortStringHelper _and_ still use
// UmbracoSettings UrlReplaceCharacters...
@@ -327,6 +332,9 @@ namespace Umbraco.Core
UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver(
typeof (DefaultUrlSegmentProvider));
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(
new PublishedContentModelFactoryImpl());
}
}
}
@@ -112,7 +112,8 @@ namespace Umbraco.Core.Dynamics
{
//don't log here, we return this exception because the caller may need to do something specific when
//this exception occurs.
return Attempt<TryInvokeMemberResult>.Fail(ext);
var mresult = new TryInvokeMemberResult(null, TryInvokeMemberSuccessReason.FoundExtensionMethod);
return Attempt<TryInvokeMemberResult>.Fail(mresult, ext);
}
catch (Exception ex)
{
@@ -124,7 +125,8 @@ namespace Umbraco.Core.Dynamics
sb.Append(t + ",");
}
LogHelper.Error<DynamicInstanceHelper>(sb.ToString(), ex);
return Attempt<TryInvokeMemberResult>.Fail(ex);
var mresult = new TryInvokeMemberResult(null, TryInvokeMemberSuccessReason.FoundExtensionMethod);
return Attempt<TryInvokeMemberResult>.Fail(mresult, ex);
}
}
return Attempt<TryInvokeMemberResult>.Fail();
+31 -9
View File
@@ -11,78 +11,101 @@ 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)
// note - 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<DynamicNull>()).GetEnumerator();
}
public DynamicNull Where(string predicate, params object[] values)
{
return this;
}
public DynamicNull OrderBy(string orderBy)
{
return this;
}
public DynamicNull ToContentSet()
{
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 +115,5 @@ namespace Umbraco.Core.Dynamics
{
return string.Empty;
}
}
}
+1 -1
View File
@@ -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;
}
@@ -13,7 +13,96 @@ namespace Umbraco.Core.Dynamics
/// Utility class for finding extension methods on a type to execute
/// </summary>
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<T> extension methods - because the assembly is excluded
.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
.ToArray();
}
// ORIGINAL CODE IS NOT COMPLETE, DOES NOT HANDLE GENERICS, ETC...
// so this is an attempt at fixing things, but it's not done yet
// and do we really want to do this? extension methods are not supported on dynamics, period
// we should use strongly typed content instead of dynamics.
/*
// get all extension methods for type thisType, with name name,
// accepting argsCount arguments (not counting the instance of thisType).
private static IEnumerable<MethodInfo> 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... F*XME
var methods = AllExtensionMethods
.Where(m => m.Name == name)
.Where(m => m.GetParameters().Length == argsCount)
.Where(m => MatchFirstParameter(thisType, m.GetParameters()[0].ParameterType));
// f*xme - 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<object> 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<T>(Foo foo, T t1, T t2)
// DoSomething(foo, t1, t2) => how can we match?!
return parameterType == argumentType; // f*xme of course!
}
*
*/
// BELOW IS THE ORIGINAL CODE...
/// <summary>
/// Returns all extension methods found matching the definition
/// </summary>
@@ -27,6 +116,10 @@ namespace Umbraco.Core.Dynamics
/// </remarks>
private static IEnumerable<MethodInfo> GetAllExtensionMethods(Type thisType, string name, int argumentCount, bool argsContainsThis)
{
// at *least* we can cache the extension methods discovery
var candidates = AllExtensionMethods;
/*
//only scan assemblies we know to contain extension methods (user assemblies)
var assembliesToScan = TypeFinder.GetAssembliesWithKnownExclusions();
@@ -45,6 +138,7 @@ namespace Umbraco.Core.Dynamics
//add the extension methods defined in IEnumerable
candidates = candidates.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public));
*/
//filter by name
var methodsByName = candidates.Where(m => m.Name == name);
+23 -37
View File
@@ -1,60 +1,46 @@
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;
PropertyType = type;
_type = type;
_source = source;
}
internal PropertyResult(string alias, object value, PropertyResultType type)
{
if (alias == null) throw new ArgumentNullException("alias");
if (value == null) throw new ArgumentNullException("value");
Alias = alias;
Value = value;
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; }
/// <summary>
/// Returns the value as a string output, this is used in the final rendering process of a property
/// </summary>
internal string ValueAsString
{
get
{
return Value == null ? "" : Convert.ToString(Value);
}
}
/// <summary>
/// The Id of the document for which this property belongs to
/// </summary>
public int DocumentId { get; set; }
/// <summary>
/// The alias of the document type alias for which this property belongs to
/// </summary>
public string DocumentTypeAlias { get; set; }
public string PropertyTypeAlias { get { return _source == null ? _alias : _source.PropertyTypeAlias; } }
public object DataValue { get { return _source == null ? _value : _source.DataValue; } }
public bool HasValue { get { return _source == null || _source.HasValue; } }
public object ObjectValue { get { return _source == null ? _value : _source.ObjectValue; } }
public object XPathValue { get { return ObjectValue == null ? null : ObjectValue.ToString(); } }
public string ToHtmlString()
{
return ValueAsString;
var value = ObjectValue;
return value == null ? string.Empty : value.ToString();
}
}
}
+29 -7
View File
@@ -243,23 +243,45 @@ namespace Umbraco.Core
});
}
///<summary>Finds the index of the first item matching an expression in an enumerable.</summary>
///<param name="items">The enumerable to search.</param>
///<param name="predicate">The expression to test the items against.</param>
///<returns>The index of the first matching item, or -1 if no items match.</returns>
/// <summary>
/// Finds the index of the first item matching an expression in an enumerable.
/// </summary>
/// <typeparam name="T">The type of the enumerated objects.</typeparam>
/// <param name="items">The enumerable to search.</param>
/// <param name="predicate">The expression to test the items against.</param>
/// <returns>The index of the first matching item, or -1.</returns>
public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate)
{
return FindIndex(items, 0, predicate);
}
/// <summary>
/// Finds the index of the first item matching an expression in an enumerable.
/// </summary>
/// <typeparam name="T">The type of the enumerated objects.</typeparam>
/// <param name="items">The enumerable to search.</param>
/// <param name="startIndex">The index to start at.</param>
/// <param name="predicate">The expression to test the items against.</param>
/// <returns>The index of the first matching item, or -1.</returns>
public static int FindIndex<T>(this IEnumerable<T> items, int startIndex, Func<T, bool> predicate)
{
if (items == null) throw new ArgumentNullException("items");
if (predicate == null) throw new ArgumentNullException("predicate");
if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex");
var index = startIndex;
if (index > 0)
items = items.Skip(index);
var retVal = 0;
foreach (var item in items)
{
if (predicate(item)) return retVal;
retVal++;
if (predicate(item)) return index;
index++;
}
return -1;
}
///<summary>Finds the index of the first occurence of an item in an enumerable.</summary>
///<param name="items">The enumerable to search.</param>
///<param name="item">The item to find.</param>
+122 -29
View File
@@ -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
{
/// <summary>
/// Defines a published item in Umbraco
/// Represents a cached content.
/// </summary>
/// <remarks>
/// 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)
/// <para>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).</para>
/// <para>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?</para>
/// </remarks>
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.
/// <summary>
/// Gets the content set to which the content belongs.
/// </summary>
/// <remarks>The default set consists in the siblings of the content (including the content
/// itself) ordered by <c>sortOrder</c>.</remarks>
IEnumerable<IPublishedContent> ContentSet { get; }
#endregion
#region ContentType
/// <summary>
/// Gets the content type.
/// </summary>
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; }
/// <summary>
/// Gets a value indicating whether the content is a content (aka a document) or a media.
/// </summary>
PublishedItemType ItemType { get; }
/// <summary>
/// Gets a value indicating whether the content is draft.
/// </summary>
/// <remarks>A content is draft when it is the unpublished version of a content, which may
/// have a published version, or not.</remarks>
bool IsDraft { get; }
/// <summary>
/// Gets the index of the published content within its current owning content set.
/// </summary>
/// <returns>The index of the published content within its current owning content set.</returns>
int GetIndex();
#endregion
#region Tree
/// <summary>
/// Gets the parent of the content.
/// </summary>
/// <remarks>The parent of root content is <c>null</c>.</remarks>
IPublishedContent Parent { get; }
/// <summary>
/// Gets the children of the content.
/// </summary>
/// <remarks>Children are sorted by their sortOrder.</remarks>
IEnumerable<IPublishedContent> Children { get; }
ICollection<IPublishedContentProperty> Properties { get; }
#endregion
/// <summary>
/// Returns the property value for the property alias specified
/// </summary>
/// <param name="propertyAlias"></param>
/// <returns></returns>
object this[string propertyAlias] { get; }
#region Properties
/// <summary>
/// Returns a property on the object based on an alias
/// <summary>
/// Gets the properties of the content.
/// </summary>
/// <remarks>
/// <para>Contains one <c>IPublishedProperty</c> for each property defined for the content type, including
/// inherited properties. Some properties may have no value.</para>
/// <para>The properties collection of an IPublishedContent instance should be read-only ie it is illegal
/// to add properties to the collection.</para>
/// </remarks>
ICollection<IPublishedProperty> Properties { get; }
/// <summary>
/// Gets a property identified by its alias.
/// </summary>
/// <param name="alias">The property alias.</param>
/// <returns>The property identified by the alias.</returns>
/// <remarks>
/// <para>If the content type has no property with that alias, including inherited properties, returns <c>null</c>,</para>
/// <para>otherwise return a property -- that may have no value (ie <c>HasValue</c> is <c>false</c>).</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
IPublishedProperty GetProperty(string alias);
/// <summary>
/// Gets a property identified by its alias.
/// </summary>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to navigate the tree upwards until a property with a value is found.</param>
/// <returns>The property identified by the alias.</returns>
/// <remarks>
/// <para>Navigate the tree upwards and look for a property with that alias and with a value (ie <c>HasValue</c> is <c>true</c>).
/// If found, return the property. If no property with that alias is found, having a value or not, return <c>null</c>. Otherwise
/// return the first property that was found with the alias but had no value (ie <c>HasValue</c> is <c>false</c>).</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
IPublishedProperty GetProperty(string alias, bool recurse);
/// <summary>
/// Gets the value of a property identified by its alias.
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
/// <remarks>
/// 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.
/// </remarks>
IPublishedContentProperty GetProperty(string alias);
}
/// <param name="alias">The property alias.</param>
/// <returns>The value of the property identified by the alias.</returns>
/// <remarks>
/// <para>If <c>GetProperty(alias)</c> is <c>null</c> then returns <c>null</c> else return <c>GetProperty(alias).Value</c>.</para>
/// <para>So if the property has no value, returns the default value for that property type.</para>
/// <para>This one is defined here really because we cannot define index extension methods, but all it should do is:
/// <code>var p = GetProperty(alias); return p == null ? null : p.Value;</code> and nothing else.</para>
/// <para>The recursive syntax (eg "_title") is _not_ supported here.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
object this[string alias] { get; } // todo - should obsolete this[alias] (when?)
#endregion
}
}
@@ -1,10 +0,0 @@
using System;
namespace Umbraco.Core.Models
{
public interface IPublishedContentProperty
{
string Alias { get; }
object Value { get; }
}
}
@@ -0,0 +1,60 @@
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a property of an <c>IPublishedContent</c>.
/// </summary>
public interface IPublishedProperty
{
/// <summary>
/// Gets the alias of the property.
/// </summary>
string PropertyTypeAlias { get; }
/// <summary>
/// Gets a value indicating whether the property has a value.
/// </summary>
/// <remarks>
/// <para>This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers
/// a missing value.</para>
/// <para>The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and
/// that includes whitespace-only) strings as "no value".</para>
/// <para>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).</para>
/// </remarks>
bool HasValue { get; }
/// <summary>
/// Gets the data value of the property.
/// </summary>
/// <remarks>
/// <para>The data 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.</para>
/// <para>The XmlPublishedCache raw values are strings exclusively since they come from the Xml cache.</para>
/// <para>For other caches that get their raw value from the database, it would be either a string,
/// an integer (Int32), or a date and time (DateTime).</para>
/// <para>If you're using that value, you're probably wrong, unless you're doing some internal
/// Umbraco stuff.</para>
/// </remarks>
object DataValue { get; }
/// <summary>
/// Gets the object value of the property.
/// </summary>
/// <remarks>
/// <para>The value is what you want to use when rendering content in an MVC view ie in C#.</para>
/// <para>It can be null, or any type of CLR object.</para>
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
/// </remarks>
object ObjectValue { get; }
/// <summary>
/// Gets the XPath value of the property.
/// </summary>
/// <remarks>
/// <para>The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine.</para>
/// <para>It must be either null, or a string, or an XPathNavigator.</para>
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
/// </remarks>
object XPathValue { get; }
}
}
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Provides methods to handle extended content.
/// </summary>
internal interface IPublishedContentExtended : IPublishedContent
{
/// <summary>
/// Adds a property to the extended content.
/// </summary>
/// <param name="property">The property to add.</param>
void AddProperty(IPublishedProperty property);
/// <summary>
/// Gets a value indicating whether properties were added to the extended content.
/// </summary>
bool HasAddedProperties { get; }
/// <summary>
/// Sets the content set of the extended content.
/// </summary>
/// <param name="contentSet"></param>
void SetContentSet(IEnumerable<IPublishedContent> contentSet);
/// <summary>
/// Resets the content set of the extended content.
/// </summary>
void ClearContentSet();
/// <summary>
/// Sets the index of the extended content.
/// </summary>
/// <param name="value">The index value.</param>
void SetIndex(int value);
/// <summary>
/// Resets the index of the extended content.
/// </summary>
void ClearIndex();
}
}
@@ -0,0 +1,16 @@
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Provides the model creation service.
/// </summary>
internal interface IPublishedContentModelFactory
{
/// <summary>
/// Creates a strongly-typed model representing a published content.
/// </summary>
/// <param name="content">The original published content.</param>
/// <returns>The strongly-typed model representing the published content, or the published content
/// itself it the factory has no model for that content type.</returns>
IPublishedContent CreateModel(IPublishedContent content);
}
}
@@ -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<IPublishedContent> 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<IPublishedProperty>();
_properties.Add(property);
}
bool IPublishedContentExtended.HasAddedProperties
{
get { return _properties != null; }
}
void IPublishedContentExtended.SetContentSet(IEnumerable<IPublishedContent> 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<IPublishedContent> _contentSet;
public override IEnumerable<IPublishedContent> ContentSet
{
get { return _contentSet ?? Content.ContentSet; }
}
#endregion
#region Properties
private ICollection<IPublishedProperty> _properties;
public override ICollection<IPublishedProperty> 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.PropertyTypeAlias.InvariantEquals(alias));
if (property != null) return property.HasValue ? property.ObjectValue : null;
}
return Content[alias];
}
}
public override IPublishedProperty GetProperty(string alias)
{
return _properties == null
? Content.GetProperty(alias)
: _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? Content.GetProperty(alias);
}
#endregion
}
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Represents a strongly-typed published content.
/// </summary>
/// <remarks>Every strongly-typed published content class should inherit from <c>PublishedContentModel</c>
/// (or inherit from a class that inherits from... etc.) so they are picked by the factory.</remarks>
public abstract class PublishedContentModel : PublishedContentExtended
{
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModel"/> class with
/// an original <see cref="IPublishedContent"/> instance.
/// </summary>
/// <param name="content">The original content.</param>
protected PublishedContentModel(IPublishedContent content)
: base(content)
{ }
}
}
@@ -0,0 +1,29 @@
using System;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Indicates that the class is a published content model for a specified content type.
/// </summary>
/// <remarks>By default, the name of the class is assumed to be the content type alias. The
/// <c>PublishedContentModelAttribute</c> can be used to indicate a different alias.</remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class PublishedContentModelAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModelAttribute"/> class with a content type alias.
/// </summary>
/// <param name="contentTypeAlias">The content type alias.</param>
public PublishedContentModelAttribute(string contentTypeAlias)
{
if (string.IsNullOrWhiteSpace(contentTypeAlias))
throw new ArgumentException("Argument cannot be null nor empty.", "contentTypeAlias");
ContentTypeAlias = contentTypeAlias;
}
/// <summary>
/// Gets or sets the content type alias.
/// </summary>
public string ContentTypeAlias { get; private set; }
}
}
@@ -0,0 +1,20 @@
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Provides strongly typed published content models services.
/// </summary>
internal static class PublishedContentModelFactory
{
/// <summary>
/// Creates a strongly typed published content model for an internal published content.
/// </summary>
/// <param name="content">The internal published content.</param>
/// <returns>The strongly typed published content model.</returns>
public static IPublishedContent CreateModel(IPublishedContent content)
{
return PublishedContentModelFactoryResolver.Current.HasValue
? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content)
: content;
}
}
}
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Implements a strongly typed content model factory
/// </summary>
internal class PublishedContentModelFactoryImpl : IPublishedContentModelFactory
{
//private readonly Dictionary<string, ConstructorInfo> _constructors
// = new Dictionary<string, ConstructorInfo>();
private readonly Dictionary<string, Func<IPublishedContent, IPublishedContent>> _constructors
= new Dictionary<string, Func<IPublishedContent, IPublishedContent>>();
public PublishedContentModelFactoryImpl()
{
var types = PluginManager.Current.ResolveTypes<PublishedContentModel>();
var ctorArgTypes = new[] { typeof(IPublishedContent) };
foreach (var type in types)
{
if (type.Inherits<PublishedContentModel>() == false)
throw new InvalidOperationException(string.Format("Type {0} is marked with PublishedContentModel attribute but does not inherit from PublishedContentExtended.", type.FullName));
var constructor = type.GetConstructor(ctorArgTypes);
if (constructor == null)
throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName));
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
typeName = typeName.ToLowerInvariant();
if (_constructors.ContainsKey(typeName))
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
// should work everywhere, but slow
//_constructors[typeName] = constructor;
// much faster with a dynamic method but potential MediumTrust issues
// here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object
// fast enough and works in MediumTrust
// read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/
var exprArg = Expression.Parameter(typeof(IPublishedContent), "content");
var exprNew = Expression.New(constructor, exprArg);
var expr = Expression.Lambda<Func<IPublishedContent, IPublishedContent>>(exprNew, exprArg);
var func = expr.Compile();
_constructors[typeName] = func;
}
}
public IPublishedContent CreateModel(IPublishedContent content)
{
// be case-insensitive
var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant();
//ConstructorInfo constructor;
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
// ? (IPublishedContent) constructor.Invoke(new object[] { content })
// : content;
Func<IPublishedContent, IPublishedContent> constructor;
return _constructors.TryGetValue(contentTypeAlias, out constructor)
? constructor(content)
: content;
}
}
}
@@ -0,0 +1,45 @@
using Umbraco.Core.ObjectResolution;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Resolves the IPublishedContentModelFactory object.
/// </summary>
internal class PublishedContentModelFactoryResolver : SingleObjectResolverBase<PublishedContentModelFactoryResolver, IPublishedContentModelFactory>
{
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModelFactoryResolver"/>.
/// </summary>
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
internal PublishedContentModelFactoryResolver()
: base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModelFactoryResolver"/> with a factory.
/// </summary>
/// <param name="factory">The factory.</param>
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
internal PublishedContentModelFactoryResolver(IPublishedContentModelFactory factory)
: base(factory)
{ }
/// <summary>
/// Sets the factory.
/// </summary>
/// <param name="factory">The factory.</param>
/// <remarks>For developers, at application startup.</remarks>
public void SetFactory(IPublishedContentModelFactory factory)
{
Value = factory;
}
/// <summary>
/// Gets the factory.
/// </summary>
public IPublishedContentModelFactory Factory
{
get { return Value; }
}
}
}
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Represents an ordered set of <see cref="IPublishedContent"/>.
/// </summary>
/// <typeparam name="T">The type of content.</typeparam>
public class PublishedContentOrderedSet<T> : PublishedContentSet<T>, IOrderedEnumerable<T>
where T : class, IPublishedContent
{
// ReSharper disable ParameterTypeCanBeEnumerable.Local
internal PublishedContentOrderedSet(IOrderedEnumerable<T> content)
// ReSharper restore ParameterTypeCanBeEnumerable.Local
: base(content)
{ }
// note: because we implement IOrderedEnumerable, we don't need to implement the ThenBy nor
// ThenByDescending methods here, only CreateOrderedEnumerable and that does it.
#region IOrderedEnumerable<T>
public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
return new PublishedContentOrderedSet<T>(((IOrderedEnumerable<T>)Source).CreateOrderedEnumerable(keySelector, comparer, descending));
}
#endregion
}
}
@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Represents a set of <see cref="IPublishedContent"/>.
/// </summary>
/// <typeparam name="T">The type of content.</typeparam>
/// <remarks>
/// <para>A <c>ContentSet{T}</c> is created from an <c>IEnumerable{T}</c> using the <c>ToContentSet</c>
/// extension method.</para>
/// <para>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.</para>
/// </remarks>
public class PublishedContentSet<T> : IEnumerable<T>
where T : class, IPublishedContent
{
// used by <c>ToContentSet</c> extension method to initialize a new set from an IEnumerable.
internal PublishedContentSet(IEnumerable<T> source)
{
if (source == null)
throw new ArgumentNullException("source");
Source = source;
}
#region Source
protected readonly IEnumerable<T> 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<T, IPublishedContentExtended> _xContent = new Dictionary<T, IPublishedContentExtended>();
// wrap an item, ie create the actual clone for this set
private T MapContentAsT(T t)
{
return MapContent(t) as T;
}
internal IPublishedContentExtended MapContent(T t)
{
IPublishedContentExtended extend;
if (_xContent.TryGetValue(t, out extend)) return extend;
extend = PublishedContentExtended.Extend(t, this);
var asT = extend 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.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 as T;
}).ToArray());
}
}
// indicates that the source has changed
// so the set can clear its inner caches
// should only be used by DynamicPublishedContentList
internal 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);
}
}
/// <summary>
/// Gets the number of items in the set.
/// </summary>
/// <returns>The number of items in the set.</returns>
/// <remarks>Will cause the set to be enumerated if it hasn't been already.</remarks>
public virtual int Count
{
get { return Enumerated.Length; }
}
#endregion
#region IEnumerable<T>
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)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<T, bool> predicate)
{
return MapContentAsT(Source.First(predicate));
}
public T FirstOrDefault()
{
var first = Source.FirstOrDefault();
return first == null ? null : MapContentAsT(first);
}
public T FirstOrDefault(Func<T, bool> predicate)
{
var first = Source.FirstOrDefault(predicate);
return first == null ? null : MapContentAsT(first);
}
public T Last()
{
return MapContentAsT(Source.Last());
}
public T Last(Func<T, bool> predicate)
{
return MapContentAsT(Source.Last(predicate));
}
public T LastOrDefault()
{
var last = Source.LastOrDefault();
return last == null ? null : MapContentAsT(last);
}
public T LastOrDefault(Func<T, bool> predicate)
{
var last = Source.LastOrDefault(predicate);
return last == null ? null : MapContentAsT(last);
}
public T Single()
{
return MapContentAsT(Source.Single());
}
public T Single(Func<T, bool> predicate)
{
return MapContentAsT(Source.Single(predicate));
}
public T SingleOrDefault()
{
var single = Source.SingleOrDefault();
return single == null ? null : MapContentAsT(single);
}
public T SingleOrDefault(Func<T, bool> predicate)
{
var single = Source.SingleOrDefault(predicate);
return single == null ? null : MapContentAsT(single);
}
#endregion
#region Wrap methods returning IOrderedEnumerable<T>
public PublishedContentOrderedSet<T> OrderBy<TKey>(Func<T, TKey> keySelector)
{
return new PublishedContentOrderedSet<T>(Source.OrderBy(keySelector));
}
public PublishedContentOrderedSet<T> OrderBy<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
{
return new PublishedContentOrderedSet<T>(Source.OrderBy(keySelector, comparer));
}
public PublishedContentOrderedSet<T> OrderByDescending<TKey>(Func<T, TKey> keySelector)
{
return new PublishedContentOrderedSet<T>(Source.OrderByDescending(keySelector));
}
public PublishedContentOrderedSet<T> OrderByDescending<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
{
return new PublishedContentOrderedSet<T>(Source.OrderByDescending(keySelector, comparer));
}
#endregion
}
}
@@ -0,0 +1,168 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
using System.Web.UI;
using Umbraco.Core.Cache;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Represents an <see cref="IPublishedContent"/> type.
/// </summary>
/// <remarks>Instances of the <see cref="PublishedContentType"/> class are immutable, ie
/// if the content type changes, then a new class needs to be created.</remarks>
public class PublishedContentType
{
private readonly PublishedPropertyType[] _propertyTypes;
// fast alias-to-index xref containing both the raw alias and its lowercase version
private readonly Dictionary<string, int> _indexes = new Dictionary<string, int>();
// 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<PublishedPropertyType> 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.PropertyTypeAlias] = i;
_indexes[propertyType.PropertyTypeAlias.ToLowerInvariant()] = i;
}
}
#region Content type
public int Id { get; private set; }
public string Alias { get; private set; }
#endregion
#region Properties
public IEnumerable<PublishedPropertyType> 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
// these methods are called by ContentTypeCacheRefresher and DataTypeCacheRefresher
internal static void ClearAll()
{
Logging.LogHelper.Debug<PublishedContentType>("Clear all.");
// ok and faster to do it by types, assuming noone else caches PublishedContentType instances
//ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_");
ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes<PublishedContentType>();
}
internal static void ClearContentType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear content type w/id {0}.", () => id);
// requires a predicate because the key does not contain the ID
// faster than key strings comparisons anyway
ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes<PublishedContentType>(
(key, value) => value.Id == id);
}
internal static void ClearDataType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear data type w/id {0}.", () => id);
// there is no recursion to handle here because a PublishedContentType contains *all* its
// properties ie both its own properties and those that were inherited (it's based upon an
// IContentTypeComposition) and so every PublishedContentType having a property based upon
// the cleared data type, be it local or inherited, will be cleared.
ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes<PublishedContentType>(
(key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id));
}
public static PublishedContentType Get(PublishedItemType itemType, string alias)
{
var key = string.Format("PublishedContentType_{0}_{1}",
itemType == PublishedItemType.Content ? "content" : "media", alias.ToLowerInvariant());
var type = ApplicationContext.Current.ApplicationCache.GetStaticCacheItem(key,
() => CreatePublishedContentType(itemType, alias));
return type;
}
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<string, PublishedContentType> _getPublishedContentTypeCallBack;
internal static Func<string, PublishedContentType> GetPublishedContentTypeCallback
{
get { return _getPublishedContentTypeCallBack; }
set
{
// see note above
//ClearAll();
ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_");
_getPublishedContentTypeCallBack = value;
}
}
#endregion
}
}
@@ -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<IPublishedContent>.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.
//
/// <summary>
/// Provides an abstract base class for <c>IPublishedContent</c> implementations that
/// wrap and extend another <c>IPublishedContent</c>.
/// </summary>
public abstract class PublishedContentWrapped : IPublishedContent
{
protected readonly IPublishedContent Content;
/// <summary>
/// Initialize a new instance of the <see cref="PublishedContentWrapped"/> class
/// with an <c>IPublishedContent</c> instance to wrap and extend.
/// </summary>
/// <param name="content">The content to wrap and extend.</param>
protected PublishedContentWrapped(IPublishedContent content)
{
Content = content;
}
/// <summary>
/// Gets the wrapped content.
/// </summary>
/// <returns>The wrapped content, that was passed as an argument to the constructor.</returns>
public IPublishedContent Unwrap()
{
return Content;
}
#region ContentSet
public virtual IEnumerable<IPublishedContent> 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<IPublishedContent> Children
{
get { return Content.Children; }
}
#endregion
#region Properties
public virtual ICollection<IPublishedProperty> 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
}
}
@@ -0,0 +1,31 @@
using System;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Provides a base class for <c>IPublishedProperty</c> implementations which converts and caches
/// the value source to the actual value to use when rendering content.
/// </summary>
internal abstract class PublishedPropertyBase : IPublishedProperty
{
public readonly PublishedPropertyType PropertyType;
protected PublishedPropertyBase(PublishedPropertyType propertyType)
{
if (propertyType == null)
throw new ArgumentNullException("propertyType");
PropertyType = propertyType;
}
public string PropertyTypeAlias
{
get { return PropertyType.PropertyTypeAlias; }
}
// these have to be provided by the actual implementation
public abstract bool HasValue { get; }
public abstract object DataValue { get; }
public abstract object ObjectValue { get; }
public abstract object XPathValue { get; }
}
}
@@ -0,0 +1,244 @@
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
{
/// <summary>
/// Represents an <see cref="IPublishedProperty"/> type.
/// </summary>
/// <remarks>Instances of the <see cref="PublishedPropertyType"/> class are immutable, ie
/// if the property type changes, then a new class needs to be created.</remarks>
public class PublishedPropertyType
{
public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType)
{
// PropertyEditor [1:n] DataTypeDefinition [1:n] PropertyType
ContentType = contentType;
PropertyTypeAlias = propertyType.Alias;
DataTypeId = propertyType.DataTypeDefinitionId;
PropertyEditorGuid = propertyType.DataTypeId;
//PropertyEditorAlias = propertyType.PropertyEditorAlias;
InitializeConverters();
}
// for unit tests
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, Guid propertyEditorGuid)
//internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, Alias propertyEditorAlias)
{
// ContentType to be set by PublishedContentType when creating it
PropertyTypeAlias = propertyTypeAlias;
DataTypeId = dataTypeDefinitionId;
PropertyEditorGuid = propertyEditorGuid;
//PropertyEditorAlias = PropertyEditorAlias;
InitializeConverters();
}
#region Property type
/// <summary>
/// Gets or sets the published content type containing the property type.
/// </summary>
// internally set by PublishedContentType constructor
public PublishedContentType ContentType { get; internal set; }
/// <summary>
/// Gets or sets the alias uniquely identifying the property type.
/// </summary>
public string PropertyTypeAlias { get; private set; }
/// <summary>
/// Gets or sets the identifier uniquely identifying the data type supporting the property type.
/// </summary>
public int DataTypeId { get; private set; }
/// <summary>
/// Gets or sets the guid uniquely identifying the property editor for the property type.
/// </summary>
public Guid PropertyEditorGuid { get; private set; }
/// <summary>
/// Gets or sets the alias uniquely identifying the property editor for the property type.
/// </summary>
//public string PropertyEditorAlias { get; private set; }
#endregion
#region Converters
private IPropertyValueConverter _converter;
private PropertyCacheLevel _sourceCacheLevel;
private PropertyCacheLevel _objectCacheLevel;
private PropertyCacheLevel _xpathCacheLevel;
private void InitializeConverters()
{
var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
// todo: remove Union() once we drop IPropertyEditorValueConverter support.
_converter = null;
foreach (var converter in converters.Union(GetCompatConverters()).Where(x => x.IsConverter(this)))
{
if (_converter == null)
{
_converter = converter;
}
else
{
throw new InvalidOperationException(string.Format("More than one converter for property type {0}.{1}",
ContentType.Alias, PropertyTypeAlias));
}
}
// get the cache levels, quietely fixing the inconsistencies (no need to throw, really)
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel;
if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel;
}
static PropertyCacheLevel GetCacheLevel(IPropertyValueConverter converter, PropertyCacheValue value)
{
if (converter == null)
return PropertyCacheLevel.Request;
var attr = converter.GetType().GetCustomAttributes<PropertyValueCacheAttribute>(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 _converter != null
? _converter.ConvertDataToSource(this, source, preview)
: ConvertUsingDarkMagic(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 _converter != null
? _converter.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 (_converter != null)
return _converter.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; } }
internal static object ConvertUsingDarkMagic(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 new DynamicXml(elt); // xml => return DynamicXml for compatiblity's sake
return source;
}
#endregion
#region Compat
// backward-compatibility: support IPropertyEditorValueConverter while we have to
// todo: remove once we drop IPropertyEditorValueConverter support.
IEnumerable<IPropertyValueConverter> GetCompatConverters()
{
return PropertyEditorValueConvertersResolver.HasCurrent
? PropertyEditorValueConvertersResolver.Current.Converters
.Where(x => x.IsConverterFor(PropertyEditorGuid, ContentType.Alias, PropertyTypeAlias))
.Select(x => new CompatConverter(x))
: Enumerable.Empty<IPropertyValueConverter>();
}
class CompatConverter : PropertyValueConverterBase
{
private readonly IPropertyEditorValueConverter _converter;
public CompatConverter(IPropertyEditorValueConverter converter)
{
_converter = converter;
}
public override bool IsConverter(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
}
}
+8 -1
View File
@@ -1,11 +1,18 @@
namespace Umbraco.Core.Models
{
/// <summary>
/// The type of published item
/// The type of published content, ie whether it is a content or a media.
/// </summary>
public enum PublishedItemType
{
/// <summary>
/// A content, ie what was formerly known as a document.
/// </summary>
Content,
/// <summary>
/// A media.
/// </summary>
Media
}
}
@@ -65,12 +65,12 @@ namespace Umbraco.Core.Models.Rdbms
return Date.Value;
}
if(!string.IsNullOrEmpty(VarChar))
if(string.IsNullOrEmpty(VarChar) == false)
{
return VarChar;
}
if(!string.IsNullOrEmpty(Text))
if(string.IsNullOrEmpty(Text) == false)
{
return Text;
}
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Persistence.Repositories
.From<DocumentDto>()
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
.Where<ContentVersionDto>(x => x.VersionId == versionId)
.Where<DocumentDto>(x => x.Newest == true);
.Where<DocumentDto>(x => x.Newest != true);
var dto = Database.Fetch<DocumentDto, ContentVersionDto>(sql).FirstOrDefault();
if(dto == null) return;
@@ -192,6 +192,29 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
public override void DeleteVersions(int id, DateTime versionDate)
{
var sql = new Sql()
.Select("*")
.From<DocumentDto>()
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
.Where<ContentVersionDto>(x => x.NodeId == id)
.Where<ContentVersionDto>(x => x.VersionDate < versionDate)
.Where<DocumentDto>(x => x.Newest != true);
var list = Database.Fetch<DocumentDto, ContentVersionDto>(sql);
if (list.Any() == false) return;
using (var transaction = Database.GetTransaction())
{
foreach (var dto in list)
{
PerformDeleteVersion(id, dto.VersionId);
}
transaction.Complete();
}
}
protected override void PerformDeleteVersion(int id, Guid versionId)
{
Database.Delete<PreviewXmlDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
@@ -61,7 +61,8 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql));
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql))
.ToArray(); // important so we don't iterate twice, if we don't do thsi we can end up with null vals in cache if we were caching.
}
private IEnumerable<IUser> PerformGetAllOnIds(params int[] ids)
@@ -46,6 +46,11 @@ namespace Umbraco.Core.Persistence.Repositories
var dto = Database.FirstOrDefault<ContentVersionDto>("WHERE versionId = @VersionId", new { VersionId = versionId });
if(dto == null) return;
//Ensure that the lastest version is not deleted
var latestVersionDto = Database.FirstOrDefault<ContentVersionDto>("WHERE ContentId = @Id ORDER BY VersionDate DESC", new { Id = dto.NodeId });
if(latestVersionDto.VersionId == dto.VersionId)
return;
using (var transaction = Database.GetTransaction())
{
PerformDeleteVersion(dto.NodeId, versionId);
@@ -56,7 +61,12 @@ namespace Umbraco.Core.Persistence.Repositories
public virtual void DeleteVersions(int id, DateTime versionDate)
{
var list = Database.Fetch<ContentVersionDto>("WHERE ContentId = @Id AND VersionDate < @VersionDate", new { Id = id, VersionDate = versionDate });
//Ensure that the latest version is not part of the versions being deleted
var latestVersionDto = Database.FirstOrDefault<ContentVersionDto>("WHERE ContentId = @Id ORDER BY VersionDate DESC", new { Id = id });
var list =
Database.Fetch<ContentVersionDto>(
"WHERE versionId <> @VersionId AND (ContentId = @Id AND VersionDate < @VersionDate)",
new {VersionId = latestVersionDto.VersionId, Id = id, VersionDate = versionDate});
if (list.Any() == false) return;
using (var transaction = Database.GetTransaction())
@@ -1,32 +1,30 @@
using System;
using Umbraco.Core.Dynamics;
namespace Umbraco.Core.PropertyEditors
{
[Obsolete("Use PropertyConverter instead since we no longer reference property editors by a GUID and instead by a string alias")]
public interface IPropertyEditorValueConverter
/// Maps a property source value to a data object.
/// </summary>
// todo: drop IPropertyEditorValueConverter support (when?).
[Obsolete("Use IPropertyValueConverter.")]
public interface IPropertyEditorValueConverter
{
/// <summary>
/// Returns a value indicating whether this provider applies to the specified property.
/// </summary>
/// <param name="datatypeGuid">A Guid identifying the property datatype.</param>
/// <param name="contentTypeAlias">The content type alias.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <returns>True if this provider applies to the specified property.</returns>
bool IsConverterFor(Guid datatypeGuid, string contentTypeAlias, string propertyTypeAlias);
/// <summary>
/// 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.
/// </summary>
/// <param name="propertyEditorId"></param>
/// <param name="docTypeAlias"> </param>
/// <param name="propertyTypeAlias"> </param>
/// <returns></returns>
bool IsConverterFor(Guid propertyEditorId, string docTypeAlias, string propertyTypeAlias);
/// <summary>
/// Attempts to convert the value specified into a useable value on the front-end
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
/// <remarks>
/// 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"
/// </remarks>
Attempt<object> ConvertPropertyValue(object value);
/// <param name="sourceValue">The source value.</param>
/// <returns>An <c>Attempt</c> representing the result of the conversion.</returns>
/// <remarks>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.</remarks>
Attempt<object> ConvertPropertyValue(object sourceValue);
}
}
@@ -0,0 +1,68 @@
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Provides published content properties conversion service.
/// </summary>
public interface IPropertyValueConverter
{
/// <summary>
/// Gets a value indicating whether the converter supports a property type.
/// </summary>
/// <param name="propertyType">The property type.</param>
/// <returns>A value indicating whether the converter supports a property type.</returns>
bool IsConverter(PublishedPropertyType propertyType);
/// <summary>
/// Converts a property Data value to a Source value.
/// </summary>
/// <param name="propertyType">The property type.</param>
/// <param name="source">The data value.</param>
/// <param name="preview">A value indicating whether conversion should take place in preview mode.</param>
/// <returns>The result of the conversion.</returns>
/// <remarks>
/// <para>The converter should know how to convert a <c>null</c> raw value, meaning that no
/// value has been assigned to the property. The source value can be <c>null</c>.</para>
/// <para>With the XML cache, raw values come from the XML cache and therefore are strings.</para>
/// <para>With objects caches, raw values would come from the database and therefore be either
/// ints, DateTimes, or strings.</para>
/// <para>The converter should be prepared to handle both situations.</para>
/// <para>When raw values are strings, the converter must handle empty strings, whitespace
/// strings, and xml-whitespace strings appropriately, ie it should know whether to preserve
/// whitespaces.</para>
/// </remarks>
object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview);
/// <summary>
/// Converts a property Source value to an Object value.
/// </summary>
/// <param name="propertyType">The property type.</param>
/// <param name="source">The source value.</param>
/// <param name="preview">A value indicating whether conversion should take place in preview mode.</param>
/// <returns>The result of the conversion.</returns>
/// <remarks>The converter should know how to convert a <c>null</c> source value, or any source value
/// indicating that no value has been assigned to the property. It is up to the converter to determine
/// what to return in that case: either <c>null</c>, or the default value...</remarks>
object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview);
/// <summary>
/// Converts a property Source value to an XPath value.
/// </summary>
/// <param name="propertyType">The property type.</param>
/// <param name="source">The source value.</param>
/// <param name="preview">A value indicating whether conversion should take place in preview mode.</param>
/// <returns>The result of the conversion.</returns>
/// <remarks>
/// <para>The converter should know how to convert a <c>null</c> source value, or any source value
/// indicating that no value has been assigned to the property. It is up to the converter to determine
/// what to return in that case: either <c>null</c>, or the default value...</para>
/// <para>If successful, the result should be either <c>null</c>, a string, or an <c>XPathNavigator</c>
/// instance. Whether an xml-whitespace string should be returned as <c>null</c> or litterally, is
/// up to the converter.</para>
/// <para>The converter may want to return an XML fragment that represent a part of the content tree,
/// but should pay attention not to create infinite loops that would kill XPath and XSLT.</para>
/// </remarks>
object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview);
}
}
@@ -0,0 +1,33 @@
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Specifies the acceptable level of cache for a property value.
/// </summary>
/// <remarks>By default, <c>Request</c> is assumed.</remarks>
public enum PropertyCacheLevel
{
/// <summary>
/// Indicates that the property value can be cached at the content level, ie it can be
/// cached until the content itself is modified.
/// </summary>
Content = 1,
/// <summary>
/// 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.
/// </summary>
ContentCache = 2,
/// <summary>
/// Indicates that the property value can be cached at the request level, ie it can be
/// cached for the duration of the current request.
/// </summary>
Request = 3,
/// <summary>
/// Indicates that the property value cannot be cached and has to be converted any time
/// it is requested.
/// </summary>
None = 4
}
}
@@ -0,0 +1,29 @@
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Specifies the different types of property cacheable values.
/// </summary>
public enum PropertyCacheValue
{
/// <summary>
/// All of them.
/// </summary>
All,
/// <summary>
/// The source value ie the internal value that can be used to create both the
/// object value and the xpath value.
/// </summary>
Source,
/// <summary>
/// The object value ie the strongly typed value of the property as seen when accessing content via C#.
/// </summary>
Object,
/// <summary>
/// The XPath value ie the value of the property as seen when accessing content via XPath.
/// </summary>
XPath
}
}
@@ -4,17 +4,35 @@ using Umbraco.Core.ObjectResolution;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Manages the list of legacy IPropertyEditorValueConverter's
/// </summary>
internal sealed class PropertyEditorValueConvertersResolver : ManyObjectsResolverBase<PropertyEditorValueConvertersResolver, IPropertyEditorValueConverter>
/// <summary>
/// Manages the list of IPropertyEditorValueConverter's
/// </summary>
internal sealed class PropertyEditorValueConvertersResolver : ManyObjectsResolverBase<PropertyEditorValueConvertersResolver, IPropertyEditorValueConverter>
{
public PropertyEditorValueConvertersResolver(IEnumerable<Type> converters)
/// <summary>
/// Initializes a new instance of the <see cref="PropertyEditorValueConvertersResolver"/> class with
/// an initial list of converter types.
/// </summary>
/// <param name="converters">The list of converter types</param>
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
internal PropertyEditorValueConvertersResolver(IEnumerable<Type> converters)
: base(converters)
{
}
public IEnumerable<IPropertyEditorValueConverter> Converters
{ }
/// <summary>
/// Initializes a new instance of the <see cref="PropertyEditorValueConvertersResolver"/> class with
/// an initial list of converter types.
/// </summary>
/// <param name="converters">The list of converter types</param>
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
internal PropertyEditorValueConvertersResolver(params Type[] converters)
: base(converters)
{ }
/// <summary>
/// Gets the converteres.
/// </summary>
public IEnumerable<IPropertyEditorValueConverter> Converters
{
get { return Values; }
}
@@ -0,0 +1,34 @@
using System;
using log4net.Core;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Indicates the cache level for a property cacheable value.
/// </summary>
/// <remarks>Use this attribute to mark property values converters.</remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class PropertyValueCacheAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyValueCacheAttribute"/> class with a cacheable value and a cache level.
/// </summary>
/// <param name="value">The cacheable value.</param>
/// <param name="level">The cache level.</param>
public PropertyValueCacheAttribute(PropertyCacheValue value, PropertyCacheLevel level)
{
Value = value;
Level = level;
}
/// <summary>
/// Gets or sets the cacheable value.
/// </summary>
public PropertyCacheValue Value { get; private set; }
/// <summary>
/// Gets or sets the cache level;
/// </summary>
public PropertyCacheLevel Level { get; private set; }
}
}
@@ -0,0 +1,30 @@
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Provides a default overridable implementation for <see cref="IPropertyValueConverter"/> that does nothing.
/// </summary>
public class PropertyValueConverterBase : IPropertyValueConverter
{
public virtual bool IsConverter(PublishedPropertyType propertyType)
{
return false;
}
public virtual object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
return PublishedPropertyType.ConvertUsingDarkMagic(source);
}
public virtual object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
{
return source;
}
public virtual object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
{
return source.ToString();
}
}
}
@@ -5,16 +5,34 @@ using Umbraco.Core.ObjectResolution;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Manages the list of PropertyValueConverter's
/// Resolves the IPropertyValueConverter objects.
/// </summary>
internal sealed class PropertyValueConvertersResolver : ManyObjectsResolverBase<PropertyValueConvertersResolver, PropertyValueConverter>
{
public PropertyValueConvertersResolver(IEnumerable<Type> converters)
: base(converters)
{
}
public sealed class PropertyValueConvertersResolver : ManyObjectsResolverBase<PropertyValueConvertersResolver, IPropertyValueConverter>
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyValueConvertersResolver"/> class with
/// an initial list of converter types.
/// </summary>
/// <param name="converters">The list of converter types</param>
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
internal PropertyValueConvertersResolver(IEnumerable<Type> converters)
: base(converters)
{ }
public IEnumerable<PropertyValueConverter> Converters
/// <summary>
/// Initializes a new instance of the <see cref="PropertyValueConvertersResolver"/> class with
/// an initial list of converter types.
/// </summary>
/// <param name="converters">The list of converter types</param>
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
internal PropertyValueConvertersResolver(params Type[] converters)
: base(converters)
{ }
/// <summary>
/// Gets the converters.
/// </summary>
public IEnumerable<IPropertyValueConverter> Converters
{
get { return Values; }
}
@@ -0,0 +1,26 @@
using System;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Indicates the CLR type of property object values returned by a converter.
/// </summary>
/// <remarks>Use this attribute to mark property values converters.</remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class PropertyValueTypeAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PropertyValueTypeAttribute"/> class with a type.
/// </summary>
/// <param name="type">The type.</param>
public PropertyValueTypeAttribute(Type type)
{
Type = type;
}
/// <summary>
/// Gets or sets the type.
/// </summary>
public Type Type { get; private set; }
}
}
@@ -0,0 +1,53 @@
using System;
using System.Globalization;
using System.Linq;
using System.Xml;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors.ValueConverters
{
[PropertyValueType(typeof(DateTime))]
[PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
public class DatePickerValueConverter : PropertyValueConverterBase
{
private static readonly Guid[] DataTypeGuids = new[]
{
Guid.Parse(Constants.PropertyEditors.DateTime),
Guid.Parse(Constants.PropertyEditors.Date)
};
public override bool IsConverter(PublishedPropertyType propertyType)
{
return DataTypeGuids.Contains(propertyType.PropertyEditorGuid);
}
public override 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;
}
// default ConvertSourceToObject just returns source ie a DateTime value
public override 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");
}
}
}
@@ -0,0 +1,40 @@
using System;
using System.Web;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors.ValueConverters
{
/// <summary>
/// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used.
/// </summary>
// 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)]
public class TinyMceValueConverter : PropertyValueConverterBase
{
public override bool IsConverter(PublishedPropertyType propertyType)
{
return Guid.Parse(Constants.PropertyEditors.TinyMCEv3).Equals(propertyType.PropertyEditorGuid);
}
public override 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 override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
{
// source should come from ConvertSource and be a string (or null) already
return new HtmlString(source == null ? string.Empty : (string)source);
}
public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
{
// source should come from ConvertSource and be a string (or null) already
return source;
}
}
}
@@ -0,0 +1,35 @@
using System;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors.ValueConverters
{
[PropertyValueType(typeof(bool))]
[PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
public class YesNoValueConverter : PropertyValueConverterBase
{
public override bool IsConverter(PublishedPropertyType propertyType)
{
return Guid.Parse(Constants.PropertyEditors.TrueFalse).Equals(propertyType.PropertyEditorGuid);
}
public override 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";
}
// default ConvertSourceToObject just returns source ie a boolean value
public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
{
// source should come from ConvertSource and be a boolean already
return (bool) source ? "1" : "0";
}
}
}
@@ -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
{
/// <summary>
/// Extension methods for IPublishedContent
/// </summary>
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
/// <summary>
/// Returns the recursive value of a field by iterating up the parent chain but starting at the publishedContent passed in
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="fieldname"></param>
/// <returns></returns>
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, 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;
}
}
}
-192
View File
@@ -1,192 +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.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using log4net.Util.TypeConverters;
namespace Umbraco.Core
{
/// <summary>
/// Utility class for dealing with data types and value conversions
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
internal class PublishedContentHelper : ApplicationEventHandler
{
/// <summary>
/// Used to invalidate the cache from the ICacherefresher
/// </summary>
internal static void ClearPropertyTypeCache()
{
PropertyTypeCache.Clear();
}
/// <summary>
/// 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
/// </summary>
internal static Func<string, string, string> GetDataTypeCallback = null;
private static readonly ConcurrentDictionary<Tuple<string, string, PublishedItemType>, string> PropertyTypeCache = new ConcurrentDictionary<Tuple<string, string, PublishedItemType>, string>();
/// <summary>
/// Return the GUID Id for the data type assigned to the document type with the property alias
/// </summary>
/// <param name="applicationContext"></param>
/// <param name="docTypeAlias"></param>
/// <param name="propertyAlias"></param>
/// <param name="itemType"></param>
/// <returns></returns>
internal static string GetPropertyEditor(ApplicationContext applicationContext, string docTypeAlias, string propertyAlias, PublishedItemType itemType)
{
if (GetDataTypeCallback != null)
return GetDataTypeCallback(docTypeAlias, propertyAlias);
var key = new Tuple<string, string, PublishedItemType>(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 string.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 string.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 string.Empty;
return property.PropertyEditorAlias;
});
}
/// <summary>
/// Converts the currentValue to a correctly typed value based on known registered converters, then based on known standards.
/// </summary>
/// <param name="currentValue"></param>
/// <param name="propertyDefinition"></param>
/// <returns></returns>
internal static Attempt<object> ConvertPropertyValue(object currentValue, PublishedPropertyDefinition propertyDefinition)
{
if (currentValue == null) return Attempt<object>.Fail();
//First, we need to check the v7+ PropertyValueConverters
var converters = PropertyValueConvertersResolver.Current.Converters
.Where(x => x.AssociatedPropertyEditorAlias == propertyDefinition.PropertyEditorAlias)
.ToArray();
if (converters.Any())
{
if (converters.Count() > 1)
{
throw new NotSupportedException("Only one " + typeof(PropertyValueConverter) + " can be registered for the property editor: " + propertyDefinition.PropertyEditorAlias);
}
var result = converters.Single().ConvertSourceToObject(
currentValue,
propertyDefinition,
false);
//if it is good return it, otherwise we'll continue processing the legacy stuff below.
if (result.Success)
{
return new Attempt<object>(true, result.Result);
}
}
//In order to maintain backwards compatibility here with IPropertyEditorValueConverter we need to attempt to lookup the
// legacy GUID for the current property editor. If one doesn't exist then we will abort the conversion.
var legacyId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(propertyDefinition.PropertyEditorAlias);
if (legacyId.HasValue == false)
{
return Attempt<object>.False;
}
//First lets check all registered converters for this data type.
var legacyConverters = PropertyEditorValueConvertersResolver.Current.Converters
.Where(x => x.IsConverterFor(legacyId.Value, propertyDefinition.DocumentTypeAlias, propertyDefinition.PropertyTypeAlias))
.ToArray();
//try to convert the value with any of the converters:
foreach (var converted in legacyConverters
.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<object>.Succeed(dResult);
}
}
//process string booleans as booleans
if (sResult.InvariantEquals("true"))
{
return Attempt<object>.Succeed(true);
}
if (sResult.InvariantEquals("false"))
{
return Attempt<object>.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 (UmbracoConfiguration.Current.UmbracoSettings.Scripting.NotDynamicXmlDocumentElements.Any(
tag => string.Equals(tag.Element, documentElement, StringComparison.CurrentCultureIgnoreCase)) == false)
{
return Attempt<object>.Succeed(new DynamicXml(e));
}
return Attempt<object>.Fail();
}
catch (Exception)
{
return Attempt<object>.Fail();
}
}
return Attempt<object>.Fail();
}
}
}
+4 -8
View File
@@ -830,15 +830,13 @@ namespace Umbraco.Core.Services
/// <summary>
/// Permanently deletes versions from an <see cref="IContent"/> object prior to a specific date.
/// This method will never delete the latest version of a content item.
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> object to delete versions from</param>
/// <param name="versionDate">Latest version date</param>
/// <param name="userId">Optional Id of the User deleting versions of a Content object</param>
public void DeleteVersions(int id, DateTime versionDate, int userId = 0)
{
//TODO: We should check if we are going to delete the most recent version because if that happens it means the
// entity is completely deleted and we should raise the normal Deleting/Deleted event
if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, dateToRetain: versionDate), this))
return;
@@ -856,6 +854,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Permanently deletes specific version(s) from an <see cref="IContent"/> object.
/// This method will never delete the latest version of a content item.
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> object to delete a version from</param>
/// <param name="versionId">Id of the version to delete</param>
@@ -865,8 +864,8 @@ namespace Umbraco.Core.Services
{
using (new WriteLock(Locker))
{
//TODO: We should check if we are going to delete the most recent version because if that happens it means the
// entity is completely deleted and we should raise the normal Deleting/Deleted event
if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this))
return;
if (deletePriorVersions)
{
@@ -874,9 +873,6 @@ namespace Umbraco.Core.Services
DeleteVersions(id, content.UpdateDate, userId);
}
if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this))
return;
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentRepository(uow))
{
+5 -3
View File
@@ -654,6 +654,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Permanently deletes versions from an <see cref="IMedia"/> object prior to a specific date.
/// This method will never delete the latest version of a content item.
/// </summary>
/// <param name="id">Id of the <see cref="IMedia"/> object to delete versions from</param>
/// <param name="versionDate">Latest version date</param>
@@ -677,6 +678,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Permanently deletes specific version(s) from an <see cref="IMedia"/> object.
/// This method will never delete the latest version of a content item.
/// </summary>
/// <param name="id">Id of the <see cref="IMedia"/> object to delete a version from</param>
/// <param name="versionId">Id of the version to delete</param>
@@ -684,15 +686,15 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User deleting versions of a Content object</param>
public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0)
{
if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this))
return;
if (deletePriorVersions)
{
var content = GetByVersion(versionId);
DeleteVersions(id, content.UpdateDate, userId);
}
if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion:versionId), this))
return;
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
{
+22 -13
View File
@@ -83,26 +83,35 @@ namespace Umbraco.Core
return true;
}
public static IEnumerable<Type> AllInterfaces(this Type target)
// that method is broken (will return duplicates) and useless (GetInterfaces already does the job)
//public static IEnumerable<Type> 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<Type> 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<MethodInfo> 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());
}
/// <returns>
+31 -6
View File
@@ -296,6 +296,22 @@
<Compile Include="Models\ContentTypeCompositionBase.cs" />
<Compile Include="Models\ContentTypeExtensions.cs" />
<Compile Include="Models\ContentTypeSort.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentExtended.cs" />
<Compile Include="Models\PublishedContent\PublishedPropertyBase.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryImpl.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentModelFactory.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryResolver.cs" />
<Compile Include="PropertyEditors\PropertyCacheValue.cs" />
<Compile Include="PropertyEditors\PropertyValueCacheAttribute.cs" />
<Compile Include="PropertyEditors\PropertyValueTypeAttribute.cs" />
<Compile Include="PropertyEditors\PropertyCacheLevel.cs" />
<Compile Include="PropertyEditors\PropertyValueConverterBase.cs" />
<Compile Include="PropertyEditors\ValueConverters\TinyMceValueConverter.cs" />
<Compile Include="PropertyEditors\PropertyValueConvertersResolver.cs" />
<Compile Include="PropertyEditors\IPropertyValueConverter.cs" />
<Compile Include="Models\PublishedContent\PublishedContentOrderedSet.cs" />
<Compile Include="Models\PublishedContent\PublishedContentSet.cs" />
<Compile Include="Models\Css\CssCompactor.cs" />
<Compile Include="Models\Css\CssParser.cs" />
<Compile Include="Models\Css\CssSyntax.cs" />
@@ -320,10 +336,16 @@
<Compile Include="Models\IMacroProperty.cs" />
<Compile Include="Models\IMacroPropertyType.cs" />
<Compile Include="Models\Macro.cs" />
<Compile Include="Models\PublishedContent\PublishedContentExtended.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelAttribute.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactory.cs" />
<Compile Include="Models\MacroProperty.cs" />
<Compile Include="Models\MacroPropertyCollection.cs" />
<Compile Include="Models\MacroPropertyTypeBaseTypes.cs" />
<Compile Include="Models\MacroTypes.cs" />
<Compile Include="Models\PublishedContent\PublishedContentWrapped.cs" />
<Compile Include="Models\PublishedContent\PublishedContentType.cs" />
<Compile Include="Models\PublishedContent\PublishedPropertyType.cs" />
<Compile Include="Models\Mapping\IMapperConfiguration.cs" />
<Compile Include="Models\Mapping\MapperConfiguration.cs" />
<Compile Include="Models\Membership\EntityPermission.cs" />
@@ -734,7 +756,6 @@
<Compile Include="PropertyEditors\ValidatorsResolver.cs" />
<Compile Include="PropertyEditors\PropertyValueEditor.cs" />
<Compile Include="PropertyEditors\ValueValidatorAttribute.cs" />
<Compile Include="PublishedContentExtensions.cs" />
<Compile Include="Dictionary\ICultureDictionary.cs" />
<Compile Include="Dynamics\ClassFactory.cs" />
<Compile Include="Dynamics\DynamicClass.cs" />
@@ -818,7 +839,7 @@
<Compile Include="NameValueCollectionExtensions.cs" />
<Compile Include="ObjectResolution\WeightedPluginAttribute.cs" />
<Compile Include="Persistence\PetaPoco.cs" />
<Compile Include="PropertyEditors\DatePickerPropertyValueConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\DatePickerValueConverter.cs" />
<Compile Include="PropertyEditors\IPropertyEditorValueConverter.cs" />
<Compile Include="Dynamics\ParseException.cs" />
<Compile Include="Dynamics\PropertyResult.cs" />
@@ -826,7 +847,7 @@
<Compile Include="Dynamics\Signature.cs" />
<Compile Include="ExpressionExtensions.cs" />
<Compile Include="Models\IPublishedContent.cs" />
<Compile Include="Models\IPublishedContentProperty.cs" />
<Compile Include="Models\IPublishedProperty.cs" />
<Compile Include="ActionsResolver.cs" />
<Compile Include="CacheRefreshersResolver.cs" />
<Compile Include="Configuration\GlobalSettings.cs" />
@@ -847,14 +868,12 @@
<Compile Include="Macros\PersistableMacroProperty.cs" />
<Compile Include="ObjectExtensions.cs" />
<Compile Include="PropertyEditors\PropertyEditorValueConvertersResolver.cs" />
<Compile Include="PropertyEditors\TinyMcePropertyValueConverter.cs" />
<Compile Include="PropertyEditors\YesNoPropertyValueConverter.cs" />
<Compile Include="ObjectResolution\ManyObjectsResolverBase.cs" />
<Compile Include="ObjectResolution\ObjectLifetimeScope.cs" />
<Compile Include="ObjectResolution\Resolution.cs" />
<Compile Include="ObjectResolution\ResolverBase.cs" />
<Compile Include="ObjectResolution\SingleObjectResolverBase.cs" />
<Compile Include="PublishedContentHelper.cs" />
<Compile Include="PropertyEditors\ValueConverters\YesNoValueConverter.cs" />
<Compile Include="Publishing\BasePublishingStrategy.cs" />
<Compile Include="Publishing\IPublishingStrategy.cs" />
<Compile Include="Publishing\PublishingStrategy.cs" />
@@ -985,6 +1004,12 @@
<Compile Include="WriteLock.cs" />
<Compile Include="XmlExtensions.cs" />
<Compile Include="XmlHelper.cs" />
<Compile Include="Xml\XPath\MacroNavigator.cs" />
<Compile Include="Xml\XPath\INavigableContentType.cs" />
<Compile Include="Xml\XPath\NavigableNavigator.cs" />
<Compile Include="Xml\XPath\INavigableContent.cs" />
<Compile Include="Xml\XPath\INavigableFieldType.cs" />
<Compile Include="Xml\XPath\INavigableSource.cs" />
<Compile Include="Xml\DynamicContext.cs" />
<Compile Include="Xml\XmlNamespaces.cs" />
<Compile Include="Xml\XmlNodeListFactory.cs" />
+2 -1
View File
@@ -73,7 +73,8 @@ namespace Umbraco.Core
/// <returns></returns>
internal static bool IsClientSideRequest(this Uri url)
{
// fixme - but really, is this OK? we should accept either no url, or .aspx, and everything else is out
// fixme - IsClientSideRequest should not use an hard-coded list of extensions
// a client-side request is anything that has an extension that is not .aspx?
var toIgnore = new[] { ".js", ".css", ".ico", ".png", ".jpg", ".jpeg", ".gif", ".html", ".svg" };
return toIgnore.Any(x => Path.GetExtension(url.LocalPath).InvariantEquals(x));
}
@@ -0,0 +1,59 @@
using System.Collections.Generic;
namespace Umbraco.Core.Xml.XPath
{
/// <summary>
/// Represents a content that can be navigated via XPath.
/// </summary>
interface INavigableContent
{
/// <summary>
/// Gets the unique identifier of the navigable content.
/// </summary>
/// <remarks>The root node identifier should be <c>-1</c>.</remarks>
int Id { get; }
/// <summary>
/// Gets the unique identifier of parent of the navigable content.
/// </summary>
/// <remarks>The top-level content parent identifiers should be <c>-1</c> ie the identifier
/// of the root node, whose parent identifier should in turn be <c>-1</c>.</remarks>
int ParentId { get; }
/// <summary>
/// Gets the type of the navigable content.
/// </summary>
INavigableContentType Type { get; }
/// <summary>
/// Gets the unique identifiers of the children of the navigable content.
/// </summary>
IList<int> ChildIds { get; }
/// <summary>
/// Gets the value of a field of the navigable content for XPath navigation use.
/// </summary>
/// <param name="index">The field index.</param>
/// <returns>The value of the field for XPath navigation use.</returns>
/// <remarks>
/// <para>Fields are attributes or elements depending on their relative index value compared
/// to source.LastAttributeIndex.</para>
/// <para>For attributes, the value must be a string.</para>
/// <para>For elements, the value should an <c>XPathNavigator</c> instance if the field is xml
/// and has content (is not empty), <c>null</c> to indicate that the element is empty, or a string
/// which can be empty, whitespace... depending on what the data type wants to expose.</para>
/// </remarks>
object Value(int index);
// TODO implement the following one
///// <summary>
///// Gets the value of a field of the navigable content, for a specified language.
///// </summary>
///// <param name="index">The field index.</param>
///// <param name="languageKey">The language key.</param>
///// <returns>The value of the field for the specified language.</returns>
///// <remarks>...</remarks>
//object Value(int index, string languageKey);
}
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Core.Xml.XPath
{
/// <summary>
/// Represents the type of a content that can be navigated via XPath.
/// </summary>
interface INavigableContentType
{
/// <summary>
/// Gets the name of the content type.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the field types of the content type.
/// </summary>
/// <remarks>This includes the attributes and the properties.</remarks>
INavigableFieldType[] FieldTypes { get; }
}
}
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Core.Xml.XPath
{
/// <summary>
/// Represents the type of a field of a content that can be navigated via XPath.
/// </summary>
/// <remarks>A field can be an attribute or a property.</remarks>
interface INavigableFieldType
{
/// <summary>
/// Gets the name of the field type.
/// </summary>
string Name { get; }
/// <summary>
/// Gets a method to convert the field value to a string.
/// </summary>
/// <remarks>This is for built-in properties, ie attributes. User-defined properties have their
/// own way to convert their value for XPath.</remarks>
Func<object, string> XmlStringConverter { get; }
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Core.Xml.XPath
{
/// <summary>
/// Represents a source of content that can be navigated via XPath.
/// </summary>
interface INavigableSource
{
/// <summary>
/// Gets a content identified by its unique identifier.
/// </summary>
/// <param name="id">The unique identifier.</param>
/// <returns>The content identified by the unique identifier, or null.</returns>
/// <remarks>When <c>id</c> is <c>-1</c> (root content) implementations should return <c>null</c>.</remarks>
INavigableContent Get(int id);
/// <summary>
/// Gets the index of the last attribute in the fields collections.
/// </summary>
int LastAttributeIndex { get; }
/// <summary>
/// Gets the content at the root of the source.
/// </summary>
/// <remarks>That content should have unique identifier <c>-1</c> and should not be gettable,
/// ie Get(-1) should return null. Its <c>ParentId</c> should be <c>-1</c>. It should provide
/// values for the attribute fields.</remarks>
INavigableContent Root { get; }
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -93,7 +93,7 @@ namespace Umbraco.Tests.CodeFirst
//Using this attribute to hide Properties from Intellisense (when compiled?)
[EditorBrowsable(EditorBrowsableState.Never)]
public ICollection<IPublishedContentProperty> Properties
public ICollection<IPublishedProperty> 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);
}
@@ -19,7 +19,7 @@ namespace Umbraco.Tests.CodeFirst
foreach (var property in content.Properties)
{
var @alias = property.Alias;
var @alias = property.PropertyTypeAlias;
var propertyInfo = propertyInfos.FirstOrDefault(x => x.Name.ToUmbracoAlias() == @alias);
if (propertyInfo == null) continue;
@@ -27,12 +27,12 @@ namespace Umbraco.Tests.CodeFirst
object value = null;
//TODO Proper mapping of types
if (propertyInfo.PropertyType == typeof(string))
value = property.Value;
value = property.ObjectValue;
else if (propertyInfo.PropertyType == typeof(DateTime))
value = DateTime.Parse(property.Value.ToString());
value = DateTime.Parse(property.ObjectValue.ToString());
else if (propertyInfo.PropertyType == typeof(Boolean))
{
if (String.IsNullOrEmpty(property.Value.ToString()) || property.Value == "0")
if (String.IsNullOrEmpty(property.ObjectValue.ToString()) || property.ObjectValue == "0")
{
value = false;
}
@@ -1,7 +1,11 @@
using System.Linq;
using System;
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;
@@ -57,7 +61,31 @@ namespace Umbraco.Tests.CodeFirst
#region Test setup
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("siteDescription", 0, Guid.Empty),
new PublishedPropertyType("siteName", 0, Guid.Empty),
new PublishedPropertyType("articleContent", 0, Guid.Empty),
new PublishedPropertyType("articleAuthor", 0, Guid.Empty),
new PublishedPropertyType("articleDate", 0, Guid.Empty),
new PublishedPropertyType("pageTitle", 0, Guid.Empty),
};
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()
@@ -10,7 +10,7 @@ namespace Umbraco.Tests.CodeFirst.TestModels
[PropertyType(typeof(TextFieldDataType))]
public string SiteName { get; set; }
[Alias("umbSiteDescription", Name = "Site Description")]
[Alias("umbSiteDescription", Name = "Site Description")] // ignored by the mapper at the moment
[PropertyType(typeof(textfieldMultipleDataType))]
public string SiteDescription { get; set; }
}
File diff suppressed because it is too large Load Diff
@@ -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<int[]>());
Assert.IsFalse(typeof(int[]).Inherits<bool[]>());
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");
// note - currently that fails because we can't match List<T> with List<int32>
var a5 = new object[] {new List<int>()};
var m5A = GetMethodForArguments(m5, a5);
Assert.IsNotNull(m5A);
// note - should we also handle "ref" and "out" parameters?
// note - should we pay attention to array types?
}
public void TestMethod1(int value) {}
public void TestMethod2<T>(T value) {}
public void TestMethod3<T>(T value1, T value2) { }
public void TestMethod4<T1, T2>(T1 value1, T2 value2) { }
public void TestMethod5<T>(List<T> 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<object> 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)
{
// note - 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<T>
{
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); // note - 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)
{ }
}
}
@@ -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
{ }
}
}
+27 -4
View File
@@ -4,7 +4,11 @@ using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
@@ -20,11 +24,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", 0, Guid.Empty),
};
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;
}
@@ -1,6 +1,7 @@
using System;
using NUnit.Framework;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.PropertyEditors
@@ -8,42 +9,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 DatePickerPropertyValueConverter();
var converter = new DatePickerValueConverter();
var dateTime = new DateTime(2012, 11, 10, 13, 14, 15);
var result = converter.ConvertSourceToObject(date, null, false);
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 YesNoPropertyValueConverter();
var result = converter.ConvertSourceToObject(value, null, false);
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);
}
}
}
@@ -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<TDocument, TDocumentList> : PublishedContentTestBase
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
protected override string GetXmlContent(int templateId)
{
return @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT CustomDocument ANY>
<!ATTLIST CustomDocument id ID #REQUIRED>
]>
<root id=""-1"">
<Home id=""1046"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1046"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[this/is/my/alias, anotheralias]]></umbracoUrlAlias>
<umbracoNaviHide>1</umbracoNaviHide>
<siteTitle><![CDATA[This is my site]]></siteTitle>
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc="""">
<content><![CDATA[<div>This is some content</div>]]></content>
<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>
<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[only/one/alias]]></umbracoUrlAlias>
<creatorName><![CDATA[Custom data with same property name as the member name]]></creatorName>
</Home>
<CustomDocument id=""1177"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub 1"" urlName=""custom-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1177"" isDoc="""" />
<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""" />
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""">
<content><![CDATA[some content]]></content>
<blah><![CDATA[some content]]></blah>
<umbracoNaviHide>1</umbracoNaviHide>
</Home>
</Home>
<Home id=""1175"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:01"" updateDate=""2012-07-20T18:49:32"" nodeName=""Sub 2"" urlName=""sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1175"" isDoc=""""><content><![CDATA[]]></content>
</Home>
<CustomDocument id=""4444"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,4444"" isDoc="""">
<selectedNodes><![CDATA[1172,1176,1173]]></selectedNodes>
<CustomDocument id=""5555"" parentID=""1046"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""0"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,4444,5555"" isDoc="""">
</CustomDocument>
</CustomDocument>
</Home>
<CustomDocument id=""1172"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1172"" isDoc="""" />
</root>";
}
/// <summary>
/// Returns the dynamic node/document to run tests against
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Tests the internal instance level caching of returning properties
/// </summary>
/// <remarks>
/// http://issues.umbraco.org/issue/U4-1824
/// http://issues.umbraco.org/issue/U4-1825
/// </remarks>
[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);
}
/// <summary>
/// Tests the IsLast method with the result set from a Where statement
/// </summary>
[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<dynamic>)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<string, object> { { "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<string, object> { { "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<string, object> { { "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<string, object> { { "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<string, object>
{
{ "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<string, object> {{"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<IGrouping<object, dynamic>>)(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<TDocument>)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<TDocument>)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<TDocument>)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<TDocument>)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<TDocument>)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<IHtmlString>(asDynamic.Content.GetType()));
Assert.AreEqual("<div>This is some content</div>", asDynamic.Content.ToString());
}
[Test]
public void Get_Children_With_Pluralized_Alias()
{
var asDynamic = GetDynamicNode(1173);
Action<object> doAssert = d =>
{
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IEnumerable>(d));
var casted = (IEnumerable<TDocument>)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<IEnumerable>(children));
var childrenAsList = asDynamic.ChildrenAsList; //test ChildrenAsList too
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IEnumerable>(childrenAsList));
var castChildren = (IEnumerable<TDocument>)children;
Assert.AreEqual(4, castChildren.Count());
var castChildrenAsList = (IEnumerable<TDocument>)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<TDocument>)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<TDocument>)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<TDocument>)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<TDocument>)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);
}
}
/// <summary>
/// Extension methods used in tests
/// </summary>
public static class TestExtensionMethods
{
public static bool ContainsValue(this string s, int val)
{
return s.Contains(val.ToString());
}
}
}
@@ -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<DynamicNode, DynamicNodeList>
{
/// <summary>
/// We only need a new schema per fixture... speeds up testing
/// </summary>
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;
}
}
}
@@ -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<DynamicPublishedContent> doc, string custom, int i, bool b)
{
return custom + i + b;
}
}
}
@@ -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<DynamicPublishedContent, DynamicPublishedContentList>
{
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));
}
/// <summary>
/// Test class to mimic UmbracoHelper when returning docs
/// </summary>
public class TestHelper
{
private readonly DynamicPublishedContent _doc;
public TestHelper(DynamicPublishedContent doc)
{
_doc = doc;
}
public IPublishedContent GetDoc()
{
return _doc;
}
public dynamic GetDocAsDynamic()
{
return _doc.AsDynamic();
}
}
}
}
@@ -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<DynamicXmlConverterTests>
{
[Test]
public void Convert_To_Raw_String()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(
XmlHelper.StripDashesInElementOrAttributeNames(xml),
xml);
var result = dXml.TryConvertTo<RawXmlString>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result.Value);
}
[Test]
public void Convert_To_Raw_XElement()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(
XmlHelper.StripDashesInElementOrAttributeNames(xml),
xml);
var result = dXml.TryConvertTo<RawXElement>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result.Value.ToString(SaveOptions.DisableFormatting));
}
[Test]
public void Convert_To_Raw_XmlElement()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(
XmlHelper.StripDashesInElementOrAttributeNames(xml),
xml);
var result = dXml.TryConvertTo<RawXmlElement>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result.Value.OuterXml);
}
[Test]
public void Convert_To_Raw_XmlDocument()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(
XmlHelper.StripDashesInElementOrAttributeNames(xml),
xml);
var result = dXml.TryConvertTo<RawXmlDocument>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result.Value.InnerXml);
}
[Test]
public void Convert_To_String()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(xml);
var result = dXml.TryConvertTo<string>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result);
}
[Test]
public void Convert_To_XElement()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(xml);
var result = dXml.TryConvertTo<XElement>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result.ToString(SaveOptions.DisableFormatting));
}
[Test]
public void Convert_To_XmlElement()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(xml);
var result = dXml.TryConvertTo<XmlElement>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result.OuterXml);
}
[Test]
public void Convert_To_XmlDocument()
{
var xml = "<DAMP fullMedia=\"\" test-attribute=\"someValue\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
var dXml = new DynamicXml(xml);
var result = dXml.TryConvertTo<XmlDocument>();
Assert.IsTrue(result.Success);
Assert.AreEqual(xml, result.Result.InnerXml);
}
public override void TestSetup()
{
}
public override void TestTearDown()
{
}
}
}
@@ -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
{
/// <summary>
/// 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
/// </summary>
[Test]
public void Deals_With_Hyphenated_Values()
{
var xml = @"<banner1Link>
<url-picker some-attribute='Content'>
<new-window>True</new-window>
<node-id>1161</node-id>
<url>/content/</url>
<link-title>12 december Zorgbeurs Care</link-title>
</url-picker>
</banner1Link>";
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 = "<DAMP fullMedia=\"\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
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 = "<DAMP fullMedia=\"\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
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 = "<DAMP fullMedia=\"\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
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 = "<DAMP fullMedia=\"\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
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 = "<DAMP fullMedia=\"\"><mediaItem><Image id=\"1057\" version=\"d58d5c16-153e-4896-892f-a722e45a69af\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:55:29\" updateDate=\"2012-11-05T16:55:44\" nodeName=\"test12\" urlName=\"test12\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1057\"><umbracoFile>/media/54/tulips.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>620888</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:55:34\"><crop name=\"thumbCrop\" x=\"154\" y=\"1\" x2=\"922\" y2=\"768\" url=\"/media/54/tulips_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem><mediaItem><Image id=\"1055\" version=\"4df1f08a-3552-45f2-b4bf-fa980c762f4a\" parentID=\"-1\" level=\"1\" writerID=\"0\" nodeType=\"1032\" template=\"0\" sortOrder=\"1\" createDate=\"2012-11-05T16:29:58\" updateDate=\"2012-11-05T16:30:27\" nodeName=\"Test\" urlName=\"test\" writerName=\"admin\" nodeTypeAlias=\"Image\" path=\"-1,1055\"><umbracoFile>/media/41/hydrangeas.jpg</umbracoFile><umbracoWidth>1024</umbracoWidth><umbracoHeight>768</umbracoHeight><umbracoBytes>595284</umbracoBytes><umbracoExtension>jpg</umbracoExtension><newsCrops><crops date=\"2012-11-05T16:30:18\"><crop name=\"thumbCrop\" x=\"133\" y=\"0\" x2=\"902\" y2=\"768\" url=\"/media/41/hydrangeas_thumbCrop.jpg\" /></crops></newsCrops></Image></mediaItem></DAMP>";
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);
}
}
}
/// <summary>
/// Test the current Core class
/// </summary>
[Test]
public void Find_Test_Core_Class()
{
RunFindTest(x => new DynamicXml(x));
}
/// <summary>
/// Tests the macroEngines legacy class
/// </summary>
[Test]
public void Find_Test_Legacy_Class()
{
RunFindTest(x => new global::umbraco.MacroEngines.DynamicXml(x));
}
private void RunFindTest(Func<string, dynamic> getDynamicXml)
{
var xmlstring = @"<test>
<item id='1' name='test 1' value='found 1'/>
<item id='2' name='test 2' value='found 2'/>
<item id='3' name='test 3' value='found 3'/>
</test>";
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<RuntimeBinderException>(() =>
{
//this will throw because result4 is not found
var temp = result4.value;
});
}
}
}
@@ -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<LegacyExamineBackedMediaTests>
{
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
}
}
}
}
@@ -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
{
/// <summary>
/// Unit tests for IPublishedContent and extensions
/// </summary>
[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<string, string>()
{
{"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<string, string>()
{
{"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<IPublishedContentProperty>(
new List<IPublishedContentProperty>()
{
new PropertyResult("property1", "value" + indexVals, Guid.NewGuid(), PropertyResultType.UserProperty),
new PropertyResult("property2", "value" + (indexVals + 1), Guid.NewGuid(), PropertyResultType.UserProperty)
}),
Children = new List<IPublishedContent>()
};
if (createChildren)
{
d.Children = new List<IPublishedContent>()
{
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> 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<IPublishedContentProperty> Properties { get; set; }
public object this[string propertyAlias]
{
get { return GetProperty(propertyAlias).Value; }
}
public IEnumerable<IPublishedContent> Children { get; set; }
public IPublishedContentProperty GetProperty(string alias)
{
return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
}
}
}
@@ -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
{
/// <summary>
/// Abstract base class for tests for published content and published media
/// </summary>
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;
}
}
}
@@ -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
{
/// <summary>
/// Tests the methods on IPublishedContent using the DefaultPublishedContentStore
/// </summary>
[TestFixture]
public class PublishedContentTests : PublishedContentTestBase
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
protected override string GetXmlContent(int templateId)
{
return @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT CustomDocument ANY>
<!ATTLIST CustomDocument id ID #REQUIRED>
]>
<root id=""-1"">
<Home id=""1046"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1046"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[this/is/my/alias, anotheralias]]></umbracoUrlAlias>
<umbracoNaviHide>1</umbracoNaviHide>
<testRecursive><![CDATA[This is the recursive val]]></testRecursive>
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc="""">
<content><![CDATA[<div>This is some content</div>]]></content>
<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>
<testRecursive><![CDATA[]]></testRecursive>
<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[only/one/alias]]></umbracoUrlAlias>
<creatorName><![CDATA[Custom data with same property name as the member name]]></creatorName>
<testRecursive><![CDATA[]]></testRecursive>
</Home>
<CustomDocument id=""1177"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub 1"" urlName=""custom-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1177"" isDoc="""" />
<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""" />
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoNaviHide>1</umbracoNaviHide>
</Home>
</Home>
<Home id=""1175"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:08:01"" updateDate=""2012-07-20T18:49:32"" nodeName=""Sub 2"" urlName=""sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1175"" isDoc=""""><content><![CDATA[]]></content>
</Home>
<CustomDocument id=""4444"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,4444"" isDoc="""">
<selectedNodes><![CDATA[1172,1176,1173]]></selectedNodes>
</CustomDocument>
</Home>
<CustomDocument id=""1172"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1172"" isDoc="""" />
</root>";
}
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<IHtmlString>(propVal.GetType()));
Assert.AreEqual("<div>This is some content</div>", propVal.ToString());
var propVal2 = doc.GetPropertyValue<IHtmlString>("content");
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(propVal2.GetType()));
Assert.AreEqual("<div>This is some content</div>", 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<string>("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);
}
}
}
@@ -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
{
/// <summary>
/// Tests the typed extension methods on IPublishedContent using the DefaultPublishedMediaStore
/// </summary>
[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;
}
/// <summary>
/// Shared with PublishMediaStoreTests
/// </summary>
/// <param name="id"></param>
/// <param name="umbracoContext"></param>
/// <returns></returns>
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(@"<node id='3113' version='5b3e46ab-3e37-4cfa-ab70-014234b5bd33' parentID='-21' level='1' writerID='0' nodeType='1032' template='0' sortOrder='2' createDate='2010-05-19T17:32:46' updateDate='2010-05-19T17:32:46' nodeName='Another Umbraco Image' urlName='acnestressscrub' writerName='Administrator' nodeTypeAlias='Image' path='-1,-21,3113'>
<data alias='umbracoFile'><![CDATA[/media/1234/blah.pdf]]></data>
<data alias='umbracoWidth'>115</data>
<data alias='umbracoHeight'>268</data>
<data alias='umbracoBytes'>10726</data>
<data alias='umbracoExtension'>jpg</data>
</node>");
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 }));
}
}
}
@@ -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 @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT NewsArticle ANY>
<!ATTLIST NewsArticle id ID #REQUIRED>
<!ELEMENT NewsLandingPage ANY>
<!ATTLIST NewsLandingPage id ID #REQUIRED>
<!ELEMENT ContentPage ANY>
<!ATTLIST ContentPage id ID #REQUIRED>
]>
<root id=""-1"">
<Home id=""1"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""10"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1"" isDoc="""">
<siteName><![CDATA[Test site]]></siteName>
<siteDescription><![CDATA[this is a test site]]></siteDescription>
<bodyContent><![CDATA[This is some body content on the home page]]></bodyContent>
<NewsLandingPage id=""2"" parentID=""1"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""11"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""news"" urlName=""news"" writerName=""admin"" creatorName=""admin"" path=""-1,1,2"" isDoc="""">
<bodyContent><![CDATA[This is some body content on the news landing page]]></bodyContent>
<pageTitle><![CDATA[page2/alias, 2ndpagealias]]></pageTitle>
<NewsArticle id=""3"" parentID=""2"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""12"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Something happened"" urlName=""something-happened"" writerName=""admin"" creatorName=""admin"" path=""-1,1,2,3"" isDoc="""">
<articleContent><![CDATA[Some cool stuff happened today]]></articleContent>
<articleDate><![CDATA[2012-01-02 12:33:44]]></articleDate>
<articleAuthor><![CDATA[John doe]]></articleAuthor>
</NewsArticle>
<NewsArticle id=""4"" parentID=""2"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""12"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Then another thing"" urlName=""then-another-thing"" writerName=""admin"" creatorName=""admin"" path=""-1,1,2,4"" isDoc="""">
<articleContent><![CDATA[Today, other cool things occurred]]></articleContent>
<articleDate><![CDATA[2012-01-03 15:33:44]]></articleDate>
<articleAuthor><![CDATA[John Smith]]></articleAuthor>
</NewsArticle>
</NewsLandingPage>
<ContentPage id=""5"" parentID=""1"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""13"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""First Content Page"" urlName=""content-page-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1,5"" isDoc="""">
<bodyContent><![CDATA[This is some body content on the first content page]]></bodyContent>
</ContentPage>
<ContentPage id=""6"" parentID=""1"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""13"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""Second Content Page"" urlName=""content-page-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1,6"" isDoc="""">
<bodyContent><![CDATA[This is some body content on the second content page]]></bodyContent>
</ContentPage>
</Home>
</root>";
}
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<InvalidOperationException>(() => 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<IPublishedContent> GetByDocumentType(string alias)
// {
// }
//}
public enum TraversalType
{
Children,
Ancestors,
AncestorsOrSelf,
Descendants,
DescendantsOrSelf
}
public static class StronglyTypedQueryExtensions
{
private static IEnumerable<IPublishedContent> 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<T>(this IPublishedContent content, string alias, Func<IPublishedContent, T> 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<HomeContentItem> 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<NewsArticleContentItem> 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<NewsLandingPageContentItem> 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<ContentPageContentItem> ContentPages(this IPublishedContent content, TraversalType traversalType = TraversalType.Children)
{
return content.GetEnumerable("ContentPage", traversalType).Select(x => new ContentPageContentItem(x));
}
}
public class PublishedContentWrapper : IPublishedContent, IOwnerCollectionAware<IPublishedContent>
{
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<IPublishedContentProperty> Properties
{
get { return WrappedContent.Properties; }
}
public object this[string propertyAlias]
{
get { return GetProperty(propertyAlias).Value; }
}
public IEnumerable<IPublishedContent> Children
{
get { return WrappedContent.Children; }
}
public IPublishedContentProperty GetProperty(string alias)
{
return WrappedContent.GetProperty(alias);
}
private IEnumerable<IPublishedContent> _ownersCollection;
/// <summary>
/// Need to get/set the owner collection when an item is returned from the result set of a query
/// </summary>
/// <remarks>
/// Based on this issue here: http://issues.umbraco.org/issue/U4-1797
/// </remarks>
IEnumerable<IPublishedContent> IOwnerCollectionAware<IPublishedContent>.OwnersCollection
{
get
{
var publishedContentBase = WrappedContent as IOwnerCollectionAware<IPublishedContent>;
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<IPublishedContent>;
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<string>("siteName"); }
}
public string SiteDescription
{
get { return WrappedContent.GetPropertyValue<string>("siteDescription"); }
}
}
public partial class NewsLandingPageContentItem : ContentPageContentItem
{
public NewsLandingPageContentItem(IPublishedContent content)
: base(content)
{
}
public string PageTitle
{
get { return WrappedContent.GetPropertyValue<string>("pageTitle"); }
}
}
public partial class NewsArticleContentItem : PublishedContentWrapper
{
public NewsArticleContentItem(IPublishedContent content)
: base(content)
{
}
public string ArticleContent
{
get { return WrappedContent.GetPropertyValue<string>("articleContent"); }
}
public DateTime ArticleDate
{
get { return WrappedContent.GetPropertyValue<DateTime>("articleDate"); }
}
public string ArticleAuthor
{
get { return WrappedContent.GetPropertyValue<string>("articleAuthor"); }
}
}
public partial class ContentPageContentItem : PublishedContentWrapper
{
public ContentPageContentItem(IPublishedContent content)
: base(content)
{
}
public string BodyContent
{
get { return WrappedContent.GetPropertyValue<string>("bodyContent"); }
}
}
#endregion
}
@@ -4,6 +4,9 @@ using System.Xml;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
@@ -14,11 +17,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()
{
@@ -66,30 +70,23 @@ namespace Umbraco.Tests.PublishedCache
}
[SetUp]
public void SetUp()
{
TestHelper.SetupLog4NetForTests();
public override void Initialize()
{
base.Initialize();
//create the app context
ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper());
_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;
var settings = SettingsForTests.GetMockSettings();
var contentMock = Mock.Get(settings.Content);
contentMock.Setup(x => x.UseLegacyXmlSchema).Returns(false);
SettingsForTests.ConfigureSettings(settings);
_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(
@@ -99,33 +96,31 @@ namespace Umbraco.Tests.PublishedCache
new WebSecurity(_httpContextFactory.HttpContext, ApplicationContext.Current));
_cache = _umbracoContext.ContentCache;
}
}
private void SetupForLegacy()
private void SetupForLegacy()
{
var settings = SettingsForTests.GetMockSettings();
var contentMock = Mock.Get(settings.Content);
contentMock.Setup(x => x.UseLegacyXmlSchema).Returns(true);
SettingsForTests.ConfigureSettings(settings);
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;
};
_xml = new XmlDocument();
_xml.LoadXml(GetLegacyXml());
}
protected override void FreezeResolution()
{
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
base.FreezeResolution();
}
[TearDown]
public void TearDown()
{
SettingsForTests.Reset();
UmbracoSettings.Reset();
}
[Test]
[Test]
public void Has_Content_LegacySchema()
{
SetupForLegacy();
@@ -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);
@@ -249,11 +243,11 @@ namespace Umbraco.Tests.PublishedCache
a => null,
//we're not going to test this so ignore
a => new List<IPublishedContent>(),
(dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)),
(dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)),
false),
//callback to get the children
d => children,
(dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)),
(dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)),
false);
return dicDoc;
}
@@ -8,6 +8,8 @@ using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.PublishedContent
@@ -29,7 +31,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", 0, Guid.Empty),
new PublishedPropertyType("selectedNodes", 0, Guid.Empty),
new PublishedPropertyType("umbracoUrlAlias", 0, Guid.Empty),
new PublishedPropertyType("content", 0, Guid.Parse(Constants.PropertyEditors.TinyMCEv3)),
new PublishedPropertyType("testRecursive", 0, Guid.Empty),
new PublishedPropertyType("siteTitle", 0, Guid.Empty),
new PublishedPropertyType("creatorName", 0, Guid.Empty),
new PublishedPropertyType("blah", 0, Guid.Empty), // 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 @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
@@ -6,6 +6,7 @@ using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
@@ -20,8 +21,14 @@ namespace Umbraco.Tests.PublishedContent
public override void Initialize()
{
base.Initialize();
//need to specify a different callback for testing
Umbraco.Web.PublishedContentExtensions.GetPropertyAliasesAndNames = s =>
// 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<string, string>()
{
@@ -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<IPublishedContentProperty>(
new List<IPublishedContentProperty>()
Properties = new Collection<IPublishedProperty>(
new List<IPublishedProperty>()
{
new PropertyResult("property1", "value" + indexVals, PropertyResultType.UserProperty),
new PropertyResult("property2", "value" + (indexVals + 1), PropertyResultType.UserProperty)
@@ -166,50 +174,81 @@ namespace Umbraco.Tests.PublishedContent
return d;
}
// note - could probably rewrite those tests using SolidPublishedContentCache
// l8tr...
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> 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<IPublishedContentProperty> Properties { get; set; }
IEnumerable<IPublishedContent> 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<IPublishedProperty> Properties { get; set; }
public IEnumerable<IPublishedContent> 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).ObjectValue; }
}
public IEnumerable<IPublishedContent> Children { get; set; }
public IPublishedProperty GetProperty(string alias)
{
return Properties.FirstOrDefault(x => x.PropertyTypeAlias.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<IPublishedContent> ContentSet
{
get { throw new NotImplementedException(); }
}
public PublishedContentType ContentType
{
get { throw new NotImplementedException(); }
}
}
}
}
@@ -0,0 +1,286 @@
using System.Linq;
using System.Collections.ObjectModel;
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(new PublishedContentModelFactoryImpl());
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 OfType1()
{
var content = UmbracoContext.Current.ContentCache.GetAtRoot()
.OfType<ContentType2>()
.Distinct()
.ToArray();
Assert.AreEqual(2, content.Count());
Assert.IsInstanceOf<ContentType2>(content.First());
var set = content.ToContentSet();
Assert.IsInstanceOf<ContentType2>(set.First());
Assert.AreSame(set, set.First().ContentSet);
Assert.IsInstanceOf<ContentType2Sub>(set.First().Next());
}
[Test]
public void OfType2()
{
var content = UmbracoContext.Current.ContentCache.GetAtRoot()
.OfType<ContentType2Sub>()
.Distinct()
.ToArray();
Assert.AreEqual(1, content.Count());
Assert.IsInstanceOf<ContentType2Sub>(content.First());
var set = content.ToContentSet();
Assert.IsInstanceOf<ContentType2Sub>(set.First());
}
[Test]
public void OfType()
{
var content = UmbracoContext.Current.ContentCache.GetAtRoot()
.OfType<ContentType2>()
.First(x => x.Prop1 == 1234);
Assert.AreEqual("Content 2", content.Name);
Assert.AreEqual(1234, content.Prop1);
}
[Test]
public void Position()
{
var content = UmbracoContext.Current.ContentCache.GetAtRoot()
.Where(x => x.GetPropertyValue<int>("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());
}
[Test]
public void Issue()
{
var content = UmbracoContext.Current.ContentCache.GetAtRoot()
.Distinct()
.OfType<ContentType2>();
var where = content.Where(x => x.Prop1 == 1234);
var first = where.First();
Assert.AreEqual(1234, first.Prop1);
var content2 = UmbracoContext.Current.ContentCache.GetAtRoot()
.OfType<ContentType2>()
.First(x => x.Prop1 == 1234);
Assert.AreEqual(1234, content2.Prop1);
var content3 = UmbracoContext.Current.ContentCache.GetAtRoot()
.OfType<ContentType2>()
.First();
Assert.AreEqual(1234, content3.Prop1);
}
static SolidPublishedCaches CreatePublishedContent()
{
var caches = new SolidPublishedCaches();
var cache = caches.ContentCache;
var props = new[]
{
new PublishedPropertyType("prop1", 1, System.Guid.Empty),
};
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<IPublishedProperty>
{
new SolidPublishedProperty
{
PropertyTypeAlias = "prop1",
HasValue = true,
ObjectValue = 1234,
DataValue = "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<IPublishedProperty>
{
new SolidPublishedProperty
{
PropertyTypeAlias = "prop1",
HasValue = true,
ObjectValue = 1234,
DataValue = "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<IPublishedProperty>
{
new SolidPublishedProperty
{
PropertyTypeAlias = "prop1",
HasValue = true,
ObjectValue = 1234,
DataValue = "1234"
}
}
});
return caches;
}
}
}
@@ -1,8 +1,12 @@
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.Core.PropertyEditors.ValueConverters;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
@@ -19,16 +23,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 Constants.PropertyEditors.TinyMCEv3Alias;
}
return string.Empty;
// AutoPublishedContentType will auto-generate other properties
new PublishedPropertyType("content", 0, Guid.Parse(Constants.PropertyEditors.TinyMCEv3)),
};
var type = new AutoPublishedContentType(0, "anything", propertyTypes);
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
var rCtx = GetRoutingContext("/test", 1234);
UmbracoContext.Current = rCtx.UmbracoContext;
@@ -40,14 +42,17 @@ namespace Umbraco.Tests.PublishedContent
PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
new[]
{
typeof(DatePickerPropertyValueConverter),
typeof(TinyMcePropertyValueConverter),
typeof(YesNoPropertyValueConverter)
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();
}
@@ -0,0 +1,301 @@
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<int, IPublishedContent> _content = new Dictionary<int, IPublishedContent>();
public void Add(SolidPublishedContent content)
{
_content[content.Id] = PublishedContentModelFactory.CreateModel(content);
}
public void Clear()
{
_content.Clear();
}
public void ContentHasChanged(UmbracoContext umbracoContext)
{
throw new NotImplementedException();
}
public IPublishedContent GetByRoute(UmbracoContext umbracoContext, bool preview, string route, bool? hideTopLevelNode = null)
{
throw new NotImplementedException();
}
public string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId)
{
throw new NotImplementedException();
}
public IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int contentId)
{
return _content.ContainsKey(contentId) ? _content[contentId] : null;
}
public IEnumerable<IPublishedContent> GetAtRoot(UmbracoContext umbracoContext, bool preview)
{
return _content.Values.Where(x => x.Parent == null);
}
public IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, Core.Xml.XPathVariable[] vars)
{
throw new NotImplementedException();
}
public IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars)
{
throw new NotImplementedException();
}
public IEnumerable<IPublishedContent> GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, Core.Xml.XPathVariable[] vars)
{
throw new NotImplementedException();
}
public IEnumerable<IPublishedContent> GetByXPath(UmbracoContext umbracoContext, bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars)
{
throw new NotImplementedException();
}
public System.Xml.XPath.XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview)
{
throw new NotImplementedException();
}
public bool XPathNavigatorIsNavigable
{
get { throw new NotImplementedException(); }
}
public bool HasContent(UmbracoContext umbracoContext, bool preview)
{
return _content.Count > 0;
}
}
class SolidPublishedContent : IPublishedContent
{
#region Constructor
public SolidPublishedContent(PublishedContentType contentType)
{
// initialize boring stuff
TemplateId = 0;
WriterName = CreatorName = string.Empty;
WriterId = CreatorId = 0;
CreateDate = UpdateDate = DateTime.Now;
Version = Guid.Empty;
IsDraft = false;
ContentType = contentType;
DocumentTypeAlias = contentType.Alias;
DocumentTypeId = contentType.Id;
}
#endregion
#region Content
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; private set; }
public int DocumentTypeId { get; private 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 string Url { get; set; }
public PublishedItemType ItemType { get { return PublishedItemType.Content; } }
public bool IsDraft { get; set; }
public int GetIndex()
{
var index = this.Siblings().FindIndex(x => x.Id == Id);
if (index < 0)
throw new IndexOutOfRangeException("Failed to find content in its siblings collection?!");
return index;
}
#endregion
#region Tree
public int ParentId { get; set; }
public IEnumerable<int> ChildIds { get; set; }
public IPublishedContent Parent { get { return UmbracoContext.Current.ContentCache.GetById(ParentId); } }
public IEnumerable<IPublishedContent> Children { get { return ChildIds.Select(id => UmbracoContext.Current.ContentCache.GetById(id)); } }
#endregion
#region ContentSet
public IEnumerable<IPublishedContent> ContentSet { get { return this.Siblings(); } }
#endregion
#region ContentType
public PublishedContentType ContentType { get; private set; }
#endregion
#region Properties
public ICollection<IPublishedProperty> Properties { get; set; }
public IPublishedProperty GetProperty(string alias)
{
return Properties.FirstOrDefault(p => p.PropertyTypeAlias.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 object this[string alias]
{
get
{
var property = GetProperty(alias);
return property == null || property.HasValue == false ? null : property.ObjectValue;
}
}
#endregion
}
class SolidPublishedProperty : IPublishedProperty
{
public SolidPublishedProperty()
{
// initialize boring stuff
}
public string PropertyTypeAlias { get; set; }
public object DataValue { get; set; }
public object ObjectValue { get; set; }
public bool HasValue { get; set; }
public object XPathValue { get; set; }
}
[PublishedContentModel("ContentType2")]
public class ContentType2 : PublishedContentModel
{
#region Plumbing
public ContentType2(IPublishedContent content)
: base(content)
{ }
#endregion
// fast, if you know that the appropriate IPropertyEditorValueConverter is wired
public int Prop1 { get { return (int)this["prop1"]; } }
// almost as fast, not sure I like it as much, though
//public int Prop1 { get { return this.GetPropertyValue<int>("prop1"); } }
}
[PublishedContentModel("ContentType2Sub")]
public class ContentType2Sub : ContentType2
{
#region Plumbing
public ContentType2Sub(IPublishedContent content)
: base(content)
{ }
#endregion
}
class PublishedContentStrong1 : PublishedContentExtended
{
public PublishedContentStrong1(IPublishedContent content)
: base(content)
{ }
public int StrongValue { get { return (int)this["strongValue"]; } }
}
class PublishedContentStrong1Sub : PublishedContentStrong1
{
public PublishedContentStrong1Sub(IPublishedContent content)
: base(content)
{ }
public int AnotherValue { get { return (int)this["anotherValue"]; } }
}
class PublishedContentStrong2 : PublishedContentExtended
{
public PublishedContentStrong2(IPublishedContent content)
: base(content)
{ }
public int StrongValue { get { return (int)this["strongValue"]; } }
}
class AutoPublishedContentType : PublishedContentType
{
private static readonly PublishedPropertyType Default = new PublishedPropertyType("*", 0, Guid.Empty);
public AutoPublishedContentType(int id, string alias, IEnumerable<PublishedPropertyType> propertyTypes)
: base(id, alias, propertyTypes)
{ }
public override PublishedPropertyType GetPropertyType(string alias)
{
var propertyType = base.GetPropertyType(alias);
return propertyType ?? Default;
}
}
}
@@ -1,8 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
@@ -19,7 +23,58 @@ namespace Umbraco.Tests.PublishedContent
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
protected override string GetXmlContent(int templateId)
private PluginManager _pluginManager;
public override void Initialize()
{
// required so we can access property.Value
//PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver();
base.Initialize();
// 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(PublishedContentTests).Assembly })
};
ApplicationContext.Current = new ApplicationContext(false) { IsReady = true };
// 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", 0, Guid.Parse(Constants.PropertyEditors.TrueFalse)),
new PublishedPropertyType("selectedNodes", 0, Guid.Empty),
new PublishedPropertyType("umbracoUrlAlias", 0, Guid.Empty),
new PublishedPropertyType("content", 0, Guid.Parse(Constants.PropertyEditors.TinyMCEv3)),
new PublishedPropertyType("testRecursive", 0, Guid.Empty),
};
var type = new AutoPublishedContentType(0, "anything", propertyTypes);
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
}
public override void TearDown()
{
PluginManager.Current = _pluginManager;
ApplicationContext.Current.DisposeIfDisposable();
ApplicationContext.Current = null;
}
protected override void FreezeResolution()
{
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(
new PublishedContentModelFactoryImpl());
base.FreezeResolution();
}
protected override string GetXmlContent(int templateId)
{
return @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
@@ -107,15 +162,17 @@ namespace Umbraco.Tests.PublishedContent
{
var doc = GetNode(1173);
foreach (var d in doc.Children.Where("Visible"))
var items = doc.Children.Where("Visible").ToContentSet();
foreach (var item in items)
{
if (d.Id != 1178)
if (item.Id != 1178)
{
Assert.IsFalse(d.IsLast());
Assert.IsFalse(item.IsLast());
}
else
{
Assert.IsTrue(d.IsLast());
Assert.IsTrue(item.IsLast());
}
}
}
@@ -125,15 +182,73 @@ namespace Umbraco.Tests.PublishedContent
{
var doc = GetNode(1173);
foreach (var d in doc.Children.Where(x => x.IsVisible()))
var items = doc
.Children
.Where(x => x.IsVisible())
.ToContentSet();
Assert.AreEqual(3, items.Count());
foreach (var d in items)
{
if (d.Id != 1178)
switch (d.Id)
{
Assert.IsFalse(d.IsLast());
case 1174:
Assert.IsTrue(d.IsFirst());
Assert.IsFalse(d.IsLast());
break;
case 1177:
Assert.IsFalse(d.IsFirst());
Assert.IsFalse(d.IsLast());
break;
case 1178:
Assert.IsFalse(d.IsFirst());
Assert.IsTrue(d.IsLast());
break;
default:
Assert.Fail("Invalid id.");
break;
}
else
}
}
[PublishedContentModel("Home")]
public class Home : PublishedContentModel
{
public Home(IPublishedContent content)
: base(content)
{}
}
[Test]
public void Is_Last_From_Where_Filter2()
{
var doc = GetNode(1173);
var items = doc.Children
.Select(PublishedContentModelFactory.CreateModel) // linq, returns IEnumerable<IPublishedContent>
// only way around this is to make sure every IEnumerable<T> extension
// explicitely returns a PublishedContentSet, not an IEnumerable<T>
.OfType<Home>() // ours, return IEnumerable<Home> (actually a PublishedContentSet<Home>)
.Where(x => x.IsVisible()) // so, here it's linq again :-(
.ToContentSet() // so, we need that one for the test to pass
.ToArray();
Assert.AreEqual(1, items.Count());
foreach (var d in items)
{
switch (d.Id)
{
Assert.IsTrue(d.IsLast());
case 1174:
Assert.IsTrue(d.IsFirst());
Assert.IsTrue(d.IsLast());
break;
default:
Assert.Fail("Invalid id.");
break;
}
}
}
@@ -143,15 +258,17 @@ namespace Umbraco.Tests.PublishedContent
{
var doc = GetNode(1173);
foreach (var d in doc.Children.Take(3))
var items = doc.Children.Take(3).ToContentSet();
foreach (var item in items)
{
if (d.Id != 1178)
if (item.Id != 1178)
{
Assert.IsFalse(d.IsLast());
Assert.IsFalse(item.IsLast());
}
else
{
Assert.IsTrue(d.IsLast());
Assert.IsTrue(item.IsLast());
}
}
}
@@ -179,16 +296,19 @@ namespace Umbraco.Tests.PublishedContent
{
var doc = GetNode(1173);
var items = doc.Children
.Concat(new[] { GetNode(1175), GetNode(4444) })
.ToContentSet();
foreach (var d in doc.Children.Concat(new[] { GetNode(1175), GetNode(4444) }))
foreach (var item in items)
{
if (d.Id != 4444)
if (item.Id != 4444)
{
Assert.IsFalse(d.IsLast());
Assert.IsFalse(item.IsLast());
}
else
{
Assert.IsTrue(d.IsLast());
Assert.IsTrue(item.IsLast());
}
}
}
@@ -234,15 +354,15 @@ namespace Umbraco.Tests.PublishedContent
var doc = GetNode(1173);
var propVal = doc.GetPropertyValue("content");
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(propVal.GetType()));
Assert.IsInstanceOf(typeof(IHtmlString), propVal);
Assert.AreEqual("<div>This is some content</div>", propVal.ToString());
var propVal2 = doc.GetPropertyValue<IHtmlString>("content");
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(propVal2.GetType()));
Assert.AreEqual("<div>This is some content</div>", propVal2.ToString());
Assert.IsInstanceOf(typeof(IHtmlString), propVal2);
Assert.AreEqual("<div>This is some content</div>", propVal2.ToString());
var propVal3 = doc.GetPropertyValue("Content");
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(propVal3.GetType()));
Assert.IsInstanceOf(typeof(IHtmlString), propVal3);
Assert.AreEqual("<div>This is some content</div>", propVal3.ToString());
}
@@ -361,7 +481,6 @@ namespace Umbraco.Tests.PublishedContent
}
[Test]
public void HasValue()
{
@@ -374,14 +493,12 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsFalse(noValue);
}
[Test]
public void Ancestors_Where_Visible()
{
var doc = GetNode(1174);
var whereVisible = doc.Ancestors().Where("Visible");
Assert.AreEqual(1, whereVisible.Count());
}
@@ -396,7 +513,6 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsTrue(visible.IsVisible());
}
[Test]
public void Ancestor_Or_Self()
{
@@ -90,15 +90,15 @@ namespace Umbraco.Tests.PublishedContent
var publishedMedia = GetNode(media.Id);
var propVal = publishedMedia.GetPropertyValue("content");
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(propVal.GetType()));
Assert.IsInstanceOf<IHtmlString>(propVal);
Assert.AreEqual("<div>This is some content</div>", propVal.ToString());
var propVal2 = publishedMedia.GetPropertyValue<IHtmlString>("content");
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(propVal2.GetType()));
Assert.IsInstanceOf<IHtmlString>(propVal2);
Assert.AreEqual("<div>This is some content</div>", propVal2.ToString());
var propVal3 = publishedMedia.GetPropertyValue("Content");
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom<IHtmlString>(propVal3.GetType()));
Assert.IsInstanceOf<IHtmlString>(propVal3);
Assert.AreEqual("<div>This is some content</div>", propVal3.ToString());
}
@@ -6,6 +6,7 @@ using System.Reflection;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web;
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
@@ -28,14 +29,11 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
/// by casting the typed model to IPublishedContent, so the properties doesn't show up by default:
/// ie. ((IPublishedContent)textpage).Url
/// </summary>
public abstract class TypedModelBase : IPublishedContent
public abstract class TypedModelBase : PublishedContentWrapped // IPublishedContent
{
private readonly IPublishedContent _publishedContent;
protected TypedModelBase(IPublishedContent publishedContent)
{
_publishedContent = publishedContent;
}
: base(publishedContent)
{ }
protected readonly Func<MethodBase> Property = MethodBase.GetCurrentMethod;
protected readonly Func<MethodBase> ContentTypeAlias = MethodBase.GetCurrentMethod;
@@ -50,7 +48,7 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
protected T Resolve<T>(string propertyTypeAlias)
{
return _publishedContent.GetPropertyValue<T>(propertyTypeAlias);
return Content.GetPropertyValue<T>(propertyTypeAlias);
}
protected T Resolve<T>(MethodBase methodBase, T ifCannotConvert)
@@ -61,7 +59,7 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
protected T Resolve<T>(string propertyTypeAlias, T ifCannotConvert)
{
return _publishedContent.GetPropertyValue<T>(propertyTypeAlias, false, ifCannotConvert);
return Content.GetPropertyValue<T>(propertyTypeAlias, false, ifCannotConvert);
}
protected T Resolve<T>(MethodBase methodBase, bool recursive, T ifCannotConvert)
@@ -72,7 +70,7 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
protected T Resolve<T>(string propertyTypeAlias, bool recursive, T ifCannotConvert)
{
return _publishedContent.GetPropertyValue<T>(propertyTypeAlias, recursive, ifCannotConvert);
return Content.GetPropertyValue<T>(propertyTypeAlias, recursive, ifCannotConvert);
}
#endregion
@@ -83,7 +81,7 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
if (constructorInfo == null)
throw new Exception("No valid constructor found");
return (T) constructorInfo.Invoke(new object[] {_publishedContent.Parent});
return (T) constructorInfo.Invoke(new object[] {Content.Parent});
}
protected IEnumerable<T> Children<T>(MethodBase methodBase) where T : TypedModelBase
@@ -100,7 +98,7 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
return _publishedContent.Children.Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
return Content.Children.Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
}
@@ -118,7 +116,7 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
return _publishedContent.Ancestors().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
return Content.Ancestors().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
}
@@ -136,42 +134,10 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
return _publishedContent.Descendants().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
return Content.Descendants().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
}
#endregion
#region IPublishedContent
int IPublishedContent.Id { get { return _publishedContent.Id; } }
int IPublishedContent.TemplateId { get { return _publishedContent.TemplateId; } }
int IPublishedContent.SortOrder { get { return _publishedContent.SortOrder; } }
string IPublishedContent.Name { get { return _publishedContent.Name; } }
string IPublishedContent.UrlName { get { return _publishedContent.UrlName; } }
string IPublishedContent.DocumentTypeAlias { get { return _publishedContent.DocumentTypeAlias; } }
int IPublishedContent.DocumentTypeId { get { return _publishedContent.DocumentTypeId; } }
string IPublishedContent.WriterName { get { return _publishedContent.WriterName; } }
string IPublishedContent.CreatorName { get { return _publishedContent.CreatorName; } }
int IPublishedContent.WriterId { get { return _publishedContent.WriterId; } }
int IPublishedContent.CreatorId { get { return _publishedContent.CreatorId; } }
string IPublishedContent.Path { get { return _publishedContent.Path; } }
DateTime IPublishedContent.CreateDate { get { return _publishedContent.CreateDate; } }
DateTime IPublishedContent.UpdateDate { get { return _publishedContent.UpdateDate; } }
Guid IPublishedContent.Version { get { return _publishedContent.Version; } }
int IPublishedContent.Level { get { return _publishedContent.Level; } }
string IPublishedContent.Url { get { return _publishedContent.Url; } }
PublishedItemType IPublishedContent.ItemType { get { return _publishedContent.ItemType; } }
IPublishedContent IPublishedContent.Parent { get { return _publishedContent.Parent; } }
IEnumerable<IPublishedContent> IPublishedContent.Children { get { return _publishedContent.Children; } }
ICollection<IPublishedContentProperty> IPublishedContent.Properties { get { return _publishedContent.Properties; } }
object IPublishedContent.this[string propertyAlias] { get { return _publishedContent[propertyAlias]; } }
IPublishedContentProperty IPublishedContent.GetProperty(string alias)
{
return _publishedContent.GetProperty(alias);
}
#endregion
}
/// <summary>
@@ -1,434 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
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 @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT NewsArticle ANY>
<!ATTLIST NewsArticle id ID #REQUIRED>
<!ELEMENT NewsLandingPage ANY>
<!ATTLIST NewsLandingPage id ID #REQUIRED>
<!ELEMENT ContentPage ANY>
<!ATTLIST ContentPage id ID #REQUIRED>
]>
<root id=""-1"">
<Home id=""1"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""10"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1"" isDoc="""">
<siteName><![CDATA[Test site]]></siteName>
<siteDescription><![CDATA[this is a test site]]></siteDescription>
<bodyContent><![CDATA[This is some body content on the home page]]></bodyContent>
<NewsLandingPage id=""2"" parentID=""1"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""11"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""news"" urlName=""news"" writerName=""admin"" creatorName=""admin"" path=""-1,1,2"" isDoc="""">
<bodyContent><![CDATA[This is some body content on the news landing page]]></bodyContent>
<pageTitle><![CDATA[page2/alias, 2ndpagealias]]></pageTitle>
<NewsArticle id=""3"" parentID=""2"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""12"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Something happened"" urlName=""something-happened"" writerName=""admin"" creatorName=""admin"" path=""-1,1,2,3"" isDoc="""">
<articleContent><![CDATA[Some cool stuff happened today]]></articleContent>
<articleDate><![CDATA[2012-01-02 12:33:44]]></articleDate>
<articleAuthor><![CDATA[John doe]]></articleAuthor>
</NewsArticle>
<NewsArticle id=""4"" parentID=""2"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""12"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Then another thing"" urlName=""then-another-thing"" writerName=""admin"" creatorName=""admin"" path=""-1,1,2,4"" isDoc="""">
<articleContent><![CDATA[Today, other cool things occurred]]></articleContent>
<articleDate><![CDATA[2012-01-03 15:33:44]]></articleDate>
<articleAuthor><![CDATA[John Smith]]></articleAuthor>
</NewsArticle>
</NewsLandingPage>
<ContentPage id=""5"" parentID=""1"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""13"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""First Content Page"" urlName=""content-page-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1,5"" isDoc="""">
<bodyContent><![CDATA[This is some body content on the first content page]]></bodyContent>
</ContentPage>
<ContentPage id=""6"" parentID=""1"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""13"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""Second Content Page"" urlName=""content-page-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1,6"" isDoc="""">
<bodyContent><![CDATA[This is some body content on the second content page]]></bodyContent>
</ContentPage>
</Home>
</root>";
}
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<InvalidOperationException>(() => 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<IPublishedContent> GetByDocumentType(string alias)
// {
// }
//}
public enum TraversalType
{
Children,
Ancestors,
AncestorsOrSelf,
Descendants,
DescendantsOrSelf
}
public static class StronglyTypedQueryExtensions
{
private static IEnumerable<IPublishedContent> 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<T>(this IPublishedContent content, string alias, Func<IPublishedContent, T> 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<HomeContentItem> 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<NewsArticleContentItem> 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<NewsLandingPageContentItem> 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<ContentPageContentItem> ContentPages(this IPublishedContent content, TraversalType traversalType = TraversalType.Children)
{
return content.GetEnumerable("ContentPage", traversalType).Select(x => new ContentPageContentItem(x));
}
}
[DebuggerDisplay("Content Id: {Id}, Name: {Name}")]
public class PublishedContentWrapper : IPublishedContent, IOwnerCollectionAware<IPublishedContent>
{
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<IPublishedContentProperty> Properties
{
get { return WrappedContent.Properties; }
}
public object this[string propertyAlias]
{
get { return GetProperty(propertyAlias).Value; }
}
public IEnumerable<IPublishedContent> Children
{
get { return WrappedContent.Children; }
}
public IPublishedContentProperty GetProperty(string alias)
{
return WrappedContent.GetProperty(alias);
}
private IEnumerable<IPublishedContent> _ownersCollection;
/// <summary>
/// Need to get/set the owner collection when an item is returned from the result set of a query
/// </summary>
/// <remarks>
/// Based on this issue here: http://issues.umbraco.org/issue/U4-1797
/// </remarks>
IEnumerable<IPublishedContent> IOwnerCollectionAware<IPublishedContent>.OwnersCollection
{
get
{
var publishedContentBase = WrappedContent as IOwnerCollectionAware<IPublishedContent>;
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<IPublishedContent>;
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<string>("siteName"); }
}
public string SiteDescription
{
get { return WrappedContent.GetPropertyValue<string>("siteDescription"); }
}
}
public partial class NewsLandingPageContentItem : ContentPageContentItem
{
public NewsLandingPageContentItem(IPublishedContent content)
: base(content)
{
}
public string PageTitle
{
get { return WrappedContent.GetPropertyValue<string>("pageTitle"); }
}
}
public partial class NewsArticleContentItem : PublishedContentWrapper
{
public NewsArticleContentItem(IPublishedContent content)
: base(content)
{
}
public string ArticleContent
{
get { return WrappedContent.GetPropertyValue<string>("articleContent"); }
}
public DateTime ArticleDate
{
get { return WrappedContent.GetPropertyValue<DateTime>("articleDate"); }
}
public string ArticleAuthor
{
get { return WrappedContent.GetPropertyValue<string>("articleAuthor"); }
}
}
public partial class ContentPageContentItem : PublishedContentWrapper
{
public ContentPageContentItem(IPublishedContent content)
: base(content)
{
}
public string BodyContent
{
get { return WrappedContent.GetPropertyValue<string>("bodyContent"); }
}
}
#endregion
}
@@ -150,6 +150,8 @@ namespace Umbraco.Tests.Publishing
var result1 = strategy.Publish(_homePage, 0);
Assert.IsTrue(result1);
Assert.IsTrue(_homePage.Published);
//NOTE (MCH) This isn't persisted, so not really a good test as it will look like the result should be something else.
foreach (var c in ServiceContext.ContentService.GetChildren(_homePage.Id))
{
var r = strategy.Publish(c, 0);
@@ -157,15 +159,20 @@ namespace Umbraco.Tests.Publishing
Assert.IsTrue(c.Published);
}
//ok, all are published except the deepest descendant, we will pass in a flag to include it to
//be published
var result = strategy.PublishWithChildrenInternal(
ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] { _homePage }), 0, true);
//there will be 4 here but only one "Success" the rest will be "SuccessAlreadyPublished"
Assert.AreEqual(1, result.Count(x => x.Result.StatusType == PublishStatusType.Success));
Assert.AreEqual(3, result.Count(x => x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished));
Assert.IsTrue(result.Single(x => x.Result.StatusType == PublishStatusType.Success).Success);
Assert.IsTrue(result.Single(x => x.Result.StatusType == PublishStatusType.Success).Result.ContentItem.Published);
//NOTE (MCH) when doing the test like this the Publish status will not actually have been persisted
//since its only updating a property. The actual persistence and publishing is done through the ContentService.
//So when descendants are fetched from the ContentService the Publish status will be "reset", which
//means the result will be 1 'SuccessAlreadyPublished' and 3 'Success' because the Homepage is
//inserted in the list and since that item has the status of already being Published it will be the one item
//with 'SuccessAlreadyPublished'
var descendants = ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] {_homePage});
var result = strategy.PublishWithChildrenInternal(descendants, 0, true);
Assert.AreEqual(3, result.Count(x => x.Result.StatusType == PublishStatusType.Success));
Assert.AreEqual(1, result.Count(x => x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished));
Assert.IsTrue(result.First(x => x.Result.StatusType == PublishStatusType.Success).Success);
Assert.IsTrue(result.First(x => x.Result.StatusType == PublishStatusType.Success).Result.ContentItem.Published);
}
[NUnit.Framework.Ignore]
@@ -52,9 +52,9 @@ namespace Umbraco.Tests.Routing
}
[TestCase("http://domain1.com/this/is/my/alias", "de-DE", -1001)] // alias to domain's page fails FIXME wanted?
[TestCase("http://domain1.com/this/is/my/alias", "de-DE", -1001)] // alias to domain's page fails - no alias on domain's home
[TestCase("http://domain1.com/page2/alias", "de-DE", 10011)] // alias to sub-page works
[TestCase("http://domain1.com/en/flux", "en-US", -10011)] // alias to domain's page fails FIXME wanted?
[TestCase("http://domain1.com/en/flux", "en-US", -10011)] // alias to domain's page fails - no alias on domain's home
[TestCase("http://domain1.com/endanger", "de-DE", 10011)] // alias to sub-page works, even with "en..."
[TestCase("http://domain1.com/en/endanger", "en-US", -10011)] // no
[TestCase("http://domain1.com/only/one/alias", "de-DE", 100111)] // ok
@@ -842,12 +842,14 @@ namespace Umbraco.Tests.Services
Assert.That(sut.GetValue<string>("multilineText"), Is.EqualTo("Multiple lines \n in one box"));
Assert.That(sut.GetValue<string>("upload"), Is.EqualTo("/media/1234/koala.jpg"));
Assert.That(sut.GetValue<string>("label"), Is.EqualTo("Non-editable label"));
Assert.That(sut.GetValue<DateTime>("dateTime"), Is.EqualTo(content.GetValue<DateTime>("dateTime")));
//SD: This is failing because the 'content' call to GetValue<DateTime> always has empty milliseconds
//MCH: I'm guessing this is an issue because of the format the date is actually stored as, right? Cause we don't do any formatting when saving or loading
Assert.That(sut.GetValue<DateTime>("dateTime").ToString("G"), Is.EqualTo(content.GetValue<DateTime>("dateTime").ToString("G")));
Assert.That(sut.GetValue<string>("colorPicker"), Is.EqualTo("black"));
Assert.That(sut.GetValue<string>("folderBrowser"), Is.Empty);
Assert.That(sut.GetValue<string>("ddlMultiple"), Is.EqualTo("1234,1235"));
Assert.That(sut.GetValue<string>("rbList"), Is.EqualTo("random"));
Assert.That(sut.GetValue<DateTime>("date"), Is.EqualTo(content.GetValue<DateTime>("date")));
Assert.That(sut.GetValue<DateTime>("date").ToString("G"), Is.EqualTo(content.GetValue<DateTime>("date").ToString("G")));
Assert.That(sut.GetValue<string>("ddl"), Is.EqualTo("1234"));
Assert.That(sut.GetValue<string>("chklist"), Is.EqualTo("randomc"));
Assert.That(sut.GetValue<int>("contentPicker"), Is.EqualTo(1090));
@@ -13,6 +13,7 @@ using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
@@ -188,6 +189,12 @@ namespace Umbraco.Tests.TestHelpers
MappingResolver.Current = new MappingResolver(
() => PluginManager.Current.ResolveAssignedMapperTypes());
if (PropertyValueConvertersResolver.HasCurrent == false)
PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver();
if (PublishedContentModelFactoryResolver.HasCurrent == false)
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
base.FreezeResolution();
}
+8 -1
View File
@@ -7,11 +7,13 @@ using SQLCE4Umbraco;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
using Umbraco.Core.Services;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.Stubs;
using Umbraco.Web;
using Umbraco.Web.Routing;
@@ -27,7 +29,12 @@ namespace Umbraco.Tests.TestHelpers
[SetUp]
public override void Initialize()
{
base.Initialize();
base.Initialize();
// 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;
}
[TearDown]
+5 -1
View File
@@ -237,6 +237,8 @@
<Compile Include="Controllers\WebApiEditors\FilterAllowedOutgoingContentAttributeTests.cs" />
<Compile Include="Controllers\WebApiEditors\MediaControllerUnitTests.cs" />
<Compile Include="CoreXml\FrameworkXmlTests.cs" />
<Compile Include="DynamicsAndReflection\ExtensionMethodFinderTests.cs" />
<Compile Include="DynamicsAndReflection\ReflectionTests.cs" />
<Compile Include="Integration\CreateContent.cs" />
<Compile Include="Macros\MacroParserTests.cs" />
<Compile Include="Migrations\Upgrades\ValidateV7UpgradeTest.cs" />
@@ -271,6 +273,7 @@
<Compile Include="Manifest\ManifestParserTests.cs" />
<Compile Include="TestHelpers\BaseSeleniumTest.cs" />
<Compile Include="Integration\InstallPackage.cs" />
<Compile Include="CoreXml\NavigableNavigatorTests.cs" />
<Compile Include="PublishedCache\PublishedMediaCacheTests.cs" />
<Compile Include="CoreStrings\CmsHelperCasingTests.cs" />
<Compile Include="CoreStrings\ShortStringHelperResolverTest.cs" />
@@ -315,9 +318,9 @@
<Compile Include="PublishedContent\LegacyExamineBackedMediaTests.cs" />
<Compile Include="PublishedContent\PublishedContentDataTableTests.cs" />
<Compile Include="PublishedContent\PublishedContentTestBase.cs" />
<Compile Include="PublishedContent\PublishedContentTestElements.cs" />
<Compile Include="PublishedContent\PublishedContentTests.cs" />
<Compile Include="PublishedContent\PublishedMediaTests.cs" />
<Compile Include="PublishedContent\StronglyTypedQueryTests.cs" />
<Compile Include="HashCodeCombinerTests.cs" />
<Compile Include="HtmlHelperExtensionMethodsTests.cs" />
<Compile Include="IO\IOHelperTest.cs" />
@@ -359,6 +362,7 @@
<Compile Include="Persistence\RepositoryResolverTests.cs" />
<Compile Include="Persistence\SqlCeTableByTableTest.cs" />
<Compile Include="Persistence\SqlTableByTableTest.cs" />
<Compile Include="PublishedContent\PublishedContentMoreTests.cs" />
<Compile Include="Publishing\PublishingStrategyTests.cs" />
<Compile Include="Resolvers\ActionsResolverTests.cs" />
<Compile Include="AsynchronousRollingFileAppenderTests.cs" />
@@ -7,6 +7,7 @@ using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Web.PublishedCache;
@@ -129,7 +130,8 @@ namespace Umbraco.Web.Cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
//clear static object cache
global::umbraco.cms.businesslogic.ContentType.RemoveAllDataTypeCache();
PublishedContentHelper.ClearPropertyTypeCache();
PublishedContentType.ClearAll();
base.RefreshAll();
}
@@ -251,7 +253,8 @@ namespace Umbraco.Web.Cache
//clears the dictionary object cache of the legacy ContentType
global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(payload.Alias);
PublishedContentHelper.ClearPropertyTypeCache();
PublishedContentType.ClearContentType(payload.Id);
//need to recursively clear the cache for each child content type
foreach (var descendant in payload.DescendantPayloads)
@@ -4,6 +4,7 @@ using Umbraco.Core;
using Umbraco.Core.Cache;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Cache
{
@@ -121,6 +122,8 @@ namespace Umbraco.Web.Cache
string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, payload.Id));
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, payload.UniqueId));
PublishedContentType.ClearDataType(payload.Id);
});
base.Refresh(jsonPayload);
@@ -21,7 +21,7 @@ namespace Umbraco.Web
public static dynamic GetDynamicById(this ContextualPublishedContentCache cache, int contentId)
{
var content = cache.GetById(contentId);
return content == null ? new DynamicNull() : new DynamicPublishedContent(content).AsDynamic();
return content == null ? DynamicNull.Null : new DynamicPublishedContent(content).AsDynamic();
}
/// <summary>
@@ -34,7 +34,7 @@ namespace Umbraco.Web
public static dynamic GetDynamicSingleByXPath(this ContextualPublishedContentCache cache, string xpath, params XPathVariable[] vars)
{
var content = cache.GetSingleByXPath(xpath, vars);
return content == null ? new DynamicNull() : new DynamicPublishedContent(content).AsDynamic();
return content == null ? DynamicNull.Null : new DynamicPublishedContent(content).AsDynamic();
}
/// <summary>
@@ -47,7 +47,7 @@ namespace Umbraco.Web
public static dynamic GetDynamicSingleByXPath(this ContextualPublishedContentCache cache, XPathExpression xpath, params XPathVariable[] vars)
{
var content = cache.GetSingleByXPath(xpath, vars);
return content == null ? new DynamicNull() : new DynamicPublishedContent(content).AsDynamic();
return content == null ? DynamicNull.Null : new DynamicPublishedContent(content).AsDynamic();
}
/// <summary>
+15 -16
View File
@@ -1,32 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
namespace Umbraco.Web.Dynamics
{
internal static class ExtensionMethods
{
public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int min, int max)
public static DynamicPublishedContentList Random(this DynamicPublishedContentList source, int min, int max)
{
//get a random number generator
Random r = new Random();
//choose the number of elements to be returned between Min and Max
int Number = r.Next(min, max);
//Call the other method
return Random(all, Number);
}
public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int max)
{
//Randomly order the items in the set by a Guid, Take the correct number, and return this wrapped in a new DynamicNodeList
return new DynamicPublishedContentList(all.Items.OrderBy(x => Guid.NewGuid()).Take(max));
return Random(source, new Random().Next(min, max));
}
public static DynamicPublishedContent Random(this DynamicPublishedContentList all)
public static DynamicPublishedContentList Random(this DynamicPublishedContentList source, int max)
{
return all.Items.OrderBy(x => Guid.NewGuid()).First();
return new DynamicPublishedContentList(source.OrderByRandom().Take(max));
}
public static DynamicPublishedContent Random(this DynamicPublishedContentList source)
{
return new DynamicPublishedContent(source.OrderByRandom().First());
}
private static IEnumerable<IPublishedContent> OrderByRandom(this DynamicPublishedContentList source)
{
return source.Items.OrderBy(x => Guid.NewGuid());
}
}
}
+25 -12
View File
@@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Examine;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web
@@ -15,25 +14,39 @@ namespace Umbraco.Web
/// </summary>
internal static class ExamineExtensions
{
internal static IEnumerable<IPublishedContent> ConvertSearchResultToPublishedContent(
this IEnumerable<SearchResult> results,
internal static PublishedContentSet<IPublishedContent> ConvertSearchResultToPublishedContent(this IEnumerable<SearchResult> results,
ContextualPublishedCache cache)
{
//TODO: The search result has already returned a result which SHOULD include all of the data to create an IPublishedContent,
// however thsi is currently not the case:
// however this is currently not the case:
// http://examine.codeplex.com/workitem/10350
var list = new List<IPublishedContent>();
var list = new List<IPublishedContent>();
var set = new PublishedContentSet<IPublishedContent>(list);
foreach (var result in results.OrderByDescending(x => x.Score))
{
var doc = cache.GetById(result.Id);
if (doc == null) continue; //skip if this doesn't exist in the cache
doc.Properties.Add(
new PropertyResult("examineScore", result.Score.ToString(), PropertyResultType.CustomProperty));
list.Add(doc);
var content = cache.GetById(result.Id);
if (content == null) continue; // skip if this doesn't exist in the cache
// need to extend the content as we're going to add a property to it,
// and we should not ever do it to the content we get from the cache,
// precisely because it is cached and shared by all requests.
// but we cannot wrap it because we need to respect the type that was
// returned by the cache, in case the cache can create real types.
// so we have to ask it to please extend itself.
list.Add(content);
var extend = set.MapContent(content);
var property = new PropertyResult("examineScore",
result.Score,
PropertyResultType.CustomProperty);
extend.AddProperty(property);
}
return list;
return set;
}
}
}

Some files were not shown because too many files have changed in this diff Show More