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:
@@ -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;
|
||||
|
||||
@@ -47,6 +47,5 @@ namespace Umbraco.Core.Cache
|
||||
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
|
||||
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-35
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user