Facade cleanup and refactoring

This commit is contained in:
Stephan
2017-09-26 14:57:50 +02:00
parent 91b8f073ad
commit c24fdc0ddf
53 changed files with 831 additions and 757 deletions

View File

@@ -13,11 +13,11 @@ namespace Umbraco.Core.Models.PublishedContent
public static bool HasCurrent => true;
public void SetFactory(IPublishedContentModelFactory factory)
public void SetFactory(IPublishedModelFactory factory)
{
CoreCurrent.Container.RegisterSingleton(_ => factory);
}
public IPublishedContentModelFactory Factory => CoreCurrent.PublishedContentModelFactory;
public IPublishedModelFactory Factory => CoreCurrent.PublishedModelFactory;
}
}

View File

@@ -125,9 +125,9 @@ namespace Umbraco.Core.Components
/// <typeparam name="T">The type of the factory.</typeparam>
/// <param name="composition">The composition.</param>
public static void SetPublishedContentModelFactory<T>(this Composition composition)
where T : IPublishedContentModelFactory
where T : IPublishedModelFactory
{
composition.Container.RegisterSingleton<IPublishedContentModelFactory, T>();
composition.Container.RegisterSingleton<IPublishedModelFactory, T>();
}
/// <summary>
@@ -135,7 +135,7 @@ namespace Umbraco.Core.Components
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a published content model factory.</param>
public static void SetPublishedContentModelFactory(this Composition composition, Func<IServiceFactory, IPublishedContentModelFactory> factory)
public static void SetPublishedContentModelFactory(this Composition composition, Func<IServiceFactory, IPublishedModelFactory> factory)
{
composition.Container.RegisterSingleton(factory);
}
@@ -145,7 +145,7 @@ namespace Umbraco.Core.Components
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A published content model factory.</param>
public static void SetPublishedContentModelFactory(this Composition composition, IPublishedContentModelFactory factory)
public static void SetPublishedContentModelFactory(this Composition composition, IPublishedModelFactory factory)
{
composition.Container.RegisterSingleton(_ => factory);
}

View File

@@ -123,8 +123,8 @@ namespace Umbraco.Core.Composing
internal static PropertyValueConverterCollection PropertyValueConverters
=> Container.GetInstance<PropertyValueConverterCollection>();
internal static IPublishedContentModelFactory PublishedContentModelFactory
=> Container.GetInstance<IPublishedContentModelFactory>();
internal static IPublishedModelFactory PublishedModelFactory
=> Container.GetInstance<IPublishedModelFactory>();
public static IServerMessenger ServerMessenger
=> Container.GetInstance<IServerMessenger>();

View File

@@ -120,7 +120,7 @@ namespace Umbraco.Core
.Append<DefaultUrlSegmentProvider>();
// by default, register a noop factory
composition.Container.RegisterSingleton<IPublishedContentModelFactory, NoopPublishedContentModelFactory>();
composition.Container.RegisterSingleton<IPublishedModelFactory, NoopPublishedModelFactory>();
}
internal void Initialize(IEnumerable<Profile> mapperProfiles)

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
namespace Umbraco.Core.Models.PublishedContent
{
/// <inheritdoc />
/// <summary>
/// Represents a cached content.
/// </summary>
@@ -17,13 +18,16 @@ namespace Umbraco.Core.Models.PublishedContent
{
#region Content
// fixme - all these are colliding with models => ?
// or could we force them to be 'new' in models?
int Id { get; }
int TemplateId { get; }
int SortOrder { get; }
string Name { get; }
string UrlName { get; }
string DocumentTypeAlias { get; }
int DocumentTypeId { get; }
string UrlName { get; } // fixme rename
string DocumentTypeAlias { get; } // fixme obsolete
int DocumentTypeId { get; } // fixme obsolete
string WriterName { get; }
string CreatorName { get; }
int WriterId { get; }

View File

@@ -1,19 +0,0 @@
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; }
}
}

View File

@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Provides the model creation service.
/// </summary>
public interface IPublishedContentModelFactory // fixme rename IFacadeModelFactory
{
/// <summary>
/// Creates a strongly-typed model representing a property set.
/// </summary>
/// <param name="set">The original property set.</param>
/// <returns>The strongly-typed model representing the property set, or the property set
/// itself it the factory has no model for that content type.</returns>
IPublishedElement CreateModel(IPublishedElement set);
/// <summary>
/// Gets the model type map.
/// </summary>
Dictionary<string, Type> ModelTypeMap { get; }
// fixme
//
// ModelFactory.Meta.Model("thing").ClrType (find the our post?)
//
// then
// make a plan to get NestedContent in
// and an equivalent of Vorto with different syntax
//
// then
// VARIANTS ARCHITECTURE FFS!
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Provides the published model creation service.
/// </summary>
public interface IPublishedModelFactory
{
/// <summary>
/// Creates a strongly-typed model representing a published element.
/// </summary>
/// <param name="element">The original published element.</param>
/// <returns>The strongly-typed model representing the published element, or the published element
/// itself it the factory has no model for the corresponding element type.</returns>
IPublishedElement CreateModel(IPublishedElement element);
/// <summary>
/// Gets the model type map.
/// </summary>
/// <remarks>The model type map maps element type aliases to actual Clr types.</remarks>
Dictionary<string, Type> ModelTypeMap { get; }
}
}

View File

@@ -1,7 +1,7 @@
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Represents a property of an <c>IPublishedContent</c>.
/// Represents a property of an <c>IPublishedElement</c>.
/// </summary>
public interface IPublishedProperty
{

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web;
namespace Umbraco.Core.Models.PublishedContent
{

View File

@@ -6,13 +6,15 @@ using System.Reflection;
namespace Umbraco.Core.Models.PublishedContent
{
// create a simple model type:
// ModelType.For("alias")
// use in a generic type:
// typeof (IEnumerable<>).MakeGenericType(ModelType.For("alias"))
// use in an array:
// ModelType.For("alias").MakeArrayType()
/// <inheritdoc />
/// <summary>
/// Represents the Clr type of a model.
/// </summary>
/// <example>
/// ModelType.For("alias")
/// typeof (IEnumerable{}).MakeGenericType(ModelType.For("alias"))
/// Model.For("alias").MakeArrayType()
/// </example>
public class ModelType : Type
{
private ModelType(string contentTypeAlias)
@@ -21,14 +23,29 @@ namespace Umbraco.Core.Models.PublishedContent
Name = "{" + ContentTypeAlias + "}";
}
/// <summary>
/// Gets the content type alias.
/// </summary>
public string ContentTypeAlias { get; }
/// <inheritdoc />
public override string ToString()
=> Name;
/// <summary>
/// Gets the model type for a published element type.
/// </summary>
/// <param name="alias">The published element type alias.</param>
/// <returns>The model type for the published element type.</returns>
public static ModelType For(string alias)
=> new ModelType(alias);
/// <summary>
/// Gets the actual Clr type by replacing model types, if any.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="modelTypes">The model types map.</param>
/// <returns>The actual Clr type.</returns>
public static Type Map(Type type, Dictionary<string, Type> modelTypes)
{
if (type is ModelType modelType)
@@ -47,12 +64,21 @@ namespace Umbraco.Core.Models.PublishedContent
if (type.IsGenericType == false)
return type;
var def = type.GetGenericTypeDefinition();
if (def == null)
throw new InvalidOperationException("panic");
var args = type.GetGenericArguments().Select(x => Map(x, modelTypes)).ToArray();
return type.GetGenericTypeDefinition().MakeGenericType(args);
return def.MakeGenericType(args);
}
/// <summary>
/// Gets a value indicating whether two <see cref="Type"/> instances are equal.
/// </summary>
/// <param name="t1">The first instance.</param>
/// <param name="t2">The second instance.</param>
/// <returns>A value indicating whether the two instances are equal.</returns>
/// <remarks>Knows how to compare <see cref="ModelType"/> instances.</remarks>
public static bool Equals(Type t1, Type t2)
{
if (t1 == t2)
@@ -80,104 +106,144 @@ namespace Umbraco.Core.Models.PublishedContent
return true;
}
/// <inheritdoc />
protected override TypeAttributes GetAttributeFlagsImpl()
=> TypeAttributes.Class;
/// <inheritdoc />
public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
=> Array.Empty<ConstructorInfo>();
/// <inheritdoc />
protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
=> null;
/// <inheritdoc />
public override Type[] GetInterfaces()
=> Array.Empty<Type>();
/// <inheritdoc />
public override Type GetInterface(string name, bool ignoreCase)
=> null;
/// <inheritdoc />
public override EventInfo[] GetEvents(BindingFlags bindingAttr)
=> Array.Empty<EventInfo>();
/// <inheritdoc />
public override EventInfo GetEvent(string name, BindingFlags bindingAttr)
=> null;
/// <inheritdoc />
public override Type[] GetNestedTypes(BindingFlags bindingAttr)
=> Array.Empty<Type>();
/// <inheritdoc />
public override Type GetNestedType(string name, BindingFlags bindingAttr)
=> null;
/// <inheritdoc />
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
=> Array.Empty<PropertyInfo>();
/// <inheritdoc />
protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
=> null;
/// <inheritdoc />
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
=> Array.Empty<MethodInfo>();
/// <inheritdoc />
protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
=> null;
/// <inheritdoc />
public override FieldInfo[] GetFields(BindingFlags bindingAttr)
=> Array.Empty<FieldInfo>();
/// <inheritdoc />
public override FieldInfo GetField(string name, BindingFlags bindingAttr)
=> null;
/// <inheritdoc />
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
=> Array.Empty<MemberInfo>();
/// <inheritdoc />
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
=> Array.Empty<object>();
/// <inheritdoc />
public override object[] GetCustomAttributes(bool inherit)
=> Array.Empty<object>();
/// <inheritdoc />
public override bool IsDefined(Type attributeType, bool inherit)
=> false;
/// <inheritdoc />
public override Type GetElementType()
=> null;
/// <inheritdoc />
protected override bool HasElementTypeImpl()
=> false;
/// <inheritdoc />
protected override bool IsArrayImpl()
=> false;
/// <inheritdoc />
protected override bool IsByRefImpl()
=> false;
/// <inheritdoc />
protected override bool IsPointerImpl()
=> false;
/// <inheritdoc />
protected override bool IsPrimitiveImpl()
=> false;
/// <inheritdoc />
protected override bool IsCOMObjectImpl()
=> false;
/// <inheritdoc />
public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
throw new NotSupportedException();
}
=> throw new NotSupportedException();
/// <inheritdoc />
public override Type UnderlyingSystemType => this;
/// <inheritdoc />
public override Type BaseType => null;
/// <inheritdoc />
public override string Name { get; }
/// <inheritdoc />
public override Guid GUID { get; } = Guid.NewGuid();
/// <inheritdoc />
public override Module Module => throw new NotSupportedException();
/// <inheritdoc />
public override Assembly Assembly => throw new NotSupportedException();
/// <inheritdoc />
public override string FullName => Name;
/// <inheritdoc />
public override string Namespace => string.Empty;
/// <inheritdoc />
public override string AssemblyQualifiedName => Name;
/// <inheritdoc />
public override Type MakeArrayType()
{
return new ModelTypeArrayType(this);
}
=> new ModelTypeArrayType(this);
}
internal class ModelTypeArrayType : Type

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Models.PublishedContent
{
public class NoopPublishedContentModelFactory : IPublishedContentModelFactory
{
public IPublishedElement CreateModel(IPublishedElement set)
=> set;
public Dictionary<string, Type> ModelTypeMap { get; } = new Dictionary<string, Type>();
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Models.PublishedContent
{
/// <inheritdoc />
/// <summary>Represents a no-operation factory.</summary>
public class NoopPublishedModelFactory : IPublishedModelFactory
{
/// <inheritdoc />
public IPublishedElement CreateModel(IPublishedElement element) => element;
/// <inheritdoc />
public Dictionary<string, Type> ModelTypeMap { get; } = new Dictionary<string, Type>();
}
}

View File

@@ -1,45 +0,0 @@
using System;
using System.Web;
namespace Umbraco.Core.Models.PublishedContent
{
// fixme - temp
internal class PropertyResult : IPublishedProperty, IHtmlString
{
private readonly IPublishedProperty _source;
private readonly string _alias;
private readonly object _value;
internal PropertyResult(IPublishedProperty source, PropertyResultType type)
{
if (source == null) throw new ArgumentNullException(nameof(source));
PropertyType = type;
_source = source;
}
internal PropertyResult(string alias, object value, PropertyResultType type)
{
if (alias == null) throw new ArgumentNullException(nameof(alias));
if (value == null) throw new ArgumentNullException(nameof(value));
PropertyType = type;
_alias = alias;
_value = value;
}
internal PropertyResultType PropertyType { get; }
public string PropertyTypeAlias => _source == null ? _alias : _source.PropertyTypeAlias;
public object SourceValue => _source == null ? _value : _source.SourceValue;
public bool HasValue => _source == null || _source.HasValue;
public object Value => _source == null ? _value : _source.Value;
public object XPathValue => Value?.ToString();
public string ToHtmlString()
{
var value = Value;
return value?.ToString() ?? string.Empty;
}
}
}

View File

@@ -1,20 +0,0 @@
namespace Umbraco.Core.Models.PublishedContent
{
internal enum PropertyResultType
{
/// <summary>
/// The property resolved was a normal document property
/// </summary>
UserProperty,
/// <summary>
/// The property resolved was a property defined as a member on the document object (IPublishedContent) itself
/// </summary>
ReflectedProperty,
/// <summary>
/// The property was created manually for a custom purpose
/// </summary>
CustomProperty
}
}

View File

@@ -20,13 +20,12 @@ namespace Umbraco.Core.Models.PublishedContent
// get model
// if factory returns nothing, throw
var model = Current.PublishedContentModelFactory.CreateModel(content);
var model = Current.PublishedModelFactory.CreateModel(content);
if (model == null)
throw new Exception("Factory returned null.");
// if factory returns a different type, throw
var publishedContent = model as IPublishedContent;
if (publishedContent == null)
if (!(model is IPublishedContent publishedContent))
throw new Exception($"Factory returned model of type {model.GetType().FullName} which does not implement IPublishedContent.");
return publishedContent;

View File

@@ -3,6 +3,7 @@ using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent
{
/// <inheritdoc />
/// <summary>
/// Indicates that the class is a published content model for a specified content type.
/// </summary>
@@ -11,8 +12,9 @@ namespace Umbraco.Core.Models.PublishedContent
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class PublishedContentModelAttribute : Attribute
{
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModelAttribute"/> class with a content type alias.
/// 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)
@@ -24,6 +26,6 @@ namespace Umbraco.Core.Models.PublishedContent
/// <summary>
/// Gets or sets the content type alias.
/// </summary>
public string ContentTypeAlias { get; private set; }
public string ContentTypeAlias { get; }
}
}

View File

@@ -1,12 +1,18 @@
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// The type of published content, ie whether it is a content or a media.
/// The type of published element.
/// </summary>
public enum PublishedItemType
/// <remarks>Can be a simple element, or a document, a media, a member.</remarks>
public enum PublishedItemType // fixme - need to rename to PublishedElementType but then conflicts?
{
/// <summary>
/// A content, ie what was formerly known as a document.
/// Unknown.
/// </summary>
Unknown = 0,
/// <summary>
/// A document.
/// </summary>
Content,

View File

@@ -2,14 +2,13 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace Umbraco.Core.Models.PublishedContent
{
/// <summary>
/// Implements a strongly typed content model factory
/// </summary>
public class PublishedContentModelFactory : IPublishedContentModelFactory
public class PublishedModelFactory : IPublishedModelFactory
{
private readonly Dictionary<string, ModelInfo> _modelInfos;
@@ -23,7 +22,7 @@ namespace Umbraco.Core.Models.PublishedContent
public Dictionary<string, Type> ModelTypeMap { get; }
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModelFactory"/> class with types.
/// Initializes a new instance of the <see cref="PublishedModelFactory"/> class with types.
/// </summary>
/// <param name="types">The model types.</param>
/// <remarks>
@@ -36,9 +35,8 @@ namespace Umbraco.Core.Models.PublishedContent
/// PublishedContentModelFactoryResolver.Current.SetFactory(factory);
/// </code>
/// </remarks>
public PublishedContentModelFactory(IEnumerable<Type> types)
public PublishedModelFactory(IEnumerable<Type> types)
{
var ctorArgTypes = new[] { typeof(IPublishedElement) };
var modelInfos = new Dictionary<string, ModelInfo>(StringComparer.InvariantCultureIgnoreCase);
var exprs = new List<Expression<Func<IPublishedElement, IPublishedElement>>>();
@@ -83,20 +81,20 @@ namespace Umbraco.Core.Models.PublishedContent
_modelInfos = modelInfos.Count > 0 ? modelInfos : null;
}
public IPublishedElement CreateModel(IPublishedElement set)
public IPublishedElement CreateModel(IPublishedElement element)
{
// fail fast
if (_modelInfos == null)
return set;
return element;
if (_modelInfos.TryGetValue(set.ContentType.Alias, out ModelInfo modelInfo) == false)
return set;
if (_modelInfos.TryGetValue(element.ContentType.Alias, out var modelInfo) == false)
return element;
// ReSharper disable once UseMethodIsInstanceOfType
if (modelInfo.ParameterType.IsAssignableFrom(set.GetType()) == false)
throw new InvalidOperationException($"Model {modelInfo.ModelType} expects argument of type {modelInfo.ParameterType.FullName}, but got {set.GetType().FullName}.");
if (modelInfo.ParameterType.IsAssignableFrom(element.GetType()) == false)
throw new InvalidOperationException($"Model {modelInfo.ModelType} expects argument of type {modelInfo.ParameterType.FullName}, but got {element.GetType().FullName}.");
return modelInfo.Ctor(set);
return modelInfo.Ctor(element);
}
}
}

View File

@@ -256,8 +256,7 @@ namespace Umbraco.Core.Models.PublishedContent
// else just return the inter value as a string or an XPathNavigator
if (inter == null) return null;
var xElement = inter as XElement;
if (xElement != null)
if (inter is XElement xElement)
return xElement.CreateNavigator();
return inter.ToString().Trim();
}
@@ -281,7 +280,7 @@ namespace Umbraco.Core.Models.PublishedContent
get
{
if (!_initialized) Initialize();
return _clrType ?? (_clrType = ModelType.Map(_modelClrType, Current.PublishedContentModelFactory.ModelTypeMap));
return _clrType ?? (_clrType = ModelType.Map(_modelClrType, Current.PublishedModelFactory.ModelTypeMap));
}
}

View File

@@ -0,0 +1,36 @@
using System;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Models.PublishedContent
{
/// <inheritdoc />
/// <summary>
/// A published property base that uses a raw object value.
/// </summary>
/// <remarks>Conversions results are stored within the property and will not
/// be refreshed, so this class is not suitable for cached properties.</remarks>
internal class RawValueProperty : PublishedPropertyBase
{
private readonly object _propertyData; //the value in the db
private readonly Lazy<object> _objectValue;
private readonly Lazy<object> _xpathValue;
public override object SourceValue => _propertyData;
public override bool HasValue => _propertyData is string s ? !string.IsNullOrWhiteSpace(s) : _propertyData != null;
public override object Value => _objectValue.Value;
public override object XPathValue => _xpathValue.Value;
public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object propertyData, bool isPreviewing = false)
: base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored
{
_propertyData = propertyData;
var interValue = new Lazy<object>(() => PropertyType.ConvertSourceToInter(content, _propertyData, isPreviewing));
_objectValue = new Lazy<object>(() => PropertyType.ConvertInterToObject(content, PropertyCacheLevel.Unknown, interValue.Value, isPreviewing));
_xpathValue = new Lazy<object>(() => PropertyType.ConvertInterToXPath(content, PropertyCacheLevel.Unknown, interValue.Value, isPreviewing));
}
}
}

View File

@@ -12,35 +12,6 @@ namespace Umbraco.Core
/// </summary>
public static class ReflectionUtilities
{
private static Func<TInstance, TValue> GetPropertyGetter<TInstance, TValue>(PropertyInfo property)
{
var type = typeof(TInstance);
var getMethod = property.GetMethod;
if (getMethod == null)
throw new InvalidOperationException($"Property {type}.{property.Name} : {property.PropertyType} does not have a getter.");
var exprThis = Expression.Parameter(type, "this");
var exprCall = Expression.Call(exprThis, getMethod);
var expr = Expression.Lambda<Func<TInstance, TValue>>(exprCall, exprThis);
return expr.CompileToDelegate();
}
private static Action<TInstance, TValue> GetPropertySetter<TInstance, TValue>(PropertyInfo property)
{
var type = typeof(TInstance);
var setMethod = property.SetMethod;
if (setMethod == null)
throw new InvalidOperationException($"Property {type}.{property.Name} : {property.PropertyType} does not have a setter.");
var exprThis = Expression.Parameter(type, "this");
var exprArg0 = Expression.Parameter(typeof(TValue), "value");
var exprCall = Expression.Call(exprThis, setMethod, exprArg0);
var expr = Expression.Lambda<Action<TInstance, TValue>>(exprCall, exprThis, exprArg0);
return expr.CompileToDelegate();
}
public static Func<TInstance, TValue> GetPropertyGetter<TInstance, TValue>(string propertyName)
{
var type = typeof(TInstance);
@@ -88,6 +59,20 @@ namespace Umbraco.Core
return expr.CompileToDelegate();
}
public static Func<object> GetCtor(Type type)
{
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, Type.EmptyTypes, null);
if (ctor == null)
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
var exprNew = Expression.New(ctor);
var expr = Expression.Lambda<Func<object>>(exprNew);
return expr.CompileToDelegate();
}
public static Func<TArg0, TInstance> GetCtor<TInstance, TArg0>()
{
var type = typeof(TInstance);
@@ -155,6 +140,35 @@ namespace Umbraco.Core
return GetMethod<TMethod>(method, methodName, type, parameterTypes, returnType);
}
private static Func<TInstance, TValue> GetPropertyGetter<TInstance, TValue>(PropertyInfo property)
{
var type = typeof(TInstance);
var getMethod = property.GetMethod;
if (getMethod == null)
throw new InvalidOperationException($"Property {type}.{property.Name} : {property.PropertyType} does not have a getter.");
var exprThis = Expression.Parameter(type, "this");
var exprCall = Expression.Call(exprThis, getMethod);
var expr = Expression.Lambda<Func<TInstance, TValue>>(exprCall, exprThis);
return expr.CompileToDelegate();
}
private static Action<TInstance, TValue> GetPropertySetter<TInstance, TValue>(PropertyInfo property)
{
var type = typeof(TInstance);
var setMethod = property.SetMethod;
if (setMethod == null)
throw new InvalidOperationException($"Property {type}.{property.Name} : {property.PropertyType} does not have a setter.");
var exprThis = Expression.Parameter(type, "this");
var exprArg0 = Expression.Parameter(typeof(TValue), "value");
var exprCall = Expression.Call(exprThis, setMethod, exprArg0);
var expr = Expression.Lambda<Action<TInstance, TValue>>(exprCall, exprThis, exprArg0);
return expr.CompileToDelegate();
}
private static void GetMethodParms<TMethod>(out Type[] parameterTypes, out Type returnType)
{
var typeM = typeof(TMethod);
@@ -260,6 +274,9 @@ namespace Umbraco.Core
return expr.Compile();
}
// not sure we want this at all?
/*
public static object GetStaticProperty(this Type type, string propertyName, Func<IEnumerable<PropertyInfo>, PropertyInfo> filter = null)
{
var propertyInfo = GetPropertyInfo(type, propertyName, filter);
@@ -418,6 +435,7 @@ namespace Umbraco.Core
propInfo.SetValue(obj, val, null);
}
*/
public static Action CompileToDelegate(Expression<Action> expr)
{

View File

@@ -644,21 +644,18 @@
<Compile Include="Models\PublicAccessRule.cs" />
<Compile Include="Models\PublishedContent\IndexedArrayItem.cs" />
<Compile Include="Models\PublishedContent\IPublishedContent.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentExtended.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentModelFactory.cs" />
<Compile Include="Models\PublishedContent\IPublishedModelFactory.cs" />
<Compile Include="Models\PublishedContent\IPublishedElement.cs" />
<Compile Include="Models\PublishedContent\IPublishedProperty.cs" />
<Compile Include="Models\PublishedContent\ModelType.cs" />
<Compile Include="Models\PublishedContent\NoopPublishedContentModelFactory.cs" />
<Compile Include="Models\PublishedContent\PropertyResult.cs" />
<Compile Include="Models\PublishedContent\PropertyResultType.cs" />
<Compile Include="Models\PublishedContent\NoopPublishedModelFactory.cs" />
<Compile Include="Models\PublishedContent\PublishedElementModel.cs" />
<Compile Include="Models\PublishedContent\PublishedElementWrapped.cs" />
<Compile Include="Models\PublishedContent\PublishedContentEnumerable.cs" />
<Compile Include="Models\PublishedContent\PublishedContentExtensionsForModels.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelAttribute.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactory.cs" />
<Compile Include="Models\PublishedContent\PublishedModelFactory.cs" />
<Compile Include="Models\PublishedContent\PublishedContentType.cs" />
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />
<Compile Include="Models\PublishedContent\PublishedContentWrapped.cs" />
@@ -666,6 +663,7 @@
<Compile Include="Models\PublishedContent\PublishedPropertyBase.cs" />
<Compile Include="Models\PublishedContent\PublishedPropertyType.cs" />
<Compile Include="Models\PublishedContent\PublishedSearchResult.cs" />
<Compile Include="Models\PublishedContent\RawValueProperty.cs" />
<Compile Include="Models\PublishedState.cs" />
<Compile Include="Models\Range.cs" />
<Compile Include="Models\Rdbms\AccessDto.cs" />

View File

@@ -61,7 +61,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
var xmlStore = new XmlStore(() => _xml);
var cacheProvider = new StaticCacheProvider();
var domainCache = new DomainCache(ServiceContext.DomainService);
var facade = new Facade(
var facade = new Umbraco.Web.PublishedCache.XmlPublishedCache.Facade(
new PublishedContentCache(xmlStore, domainCache, cacheProvider, ContentTypesCache, null, null),
new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache),
new PublishedMemberCache(null, cacheProvider, Current.Services.MemberService, ContentTypesCache),

View File

@@ -0,0 +1,290 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LightInject;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.PropertyEditors.ValueConverters;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Tests.Facade
{
[TestFixture]
public class NestedContentTests
{
private (PublishedContentType, PublishedContentType) CreateContentTypes()
{
Current.Reset();
var logger = Mock.Of<ILogger>();
var profiler = Mock.Of<IProfiler>();
var proflog = new ProfilingLogger(logger, profiler);
var container = new ServiceContainer();
container.ConfigureUmbracoCore();
// fixme - temp needed by NestedContentHelper to cache preValues
container.RegisterSingleton(f => CacheHelper.NoCache);
var dataTypeService = new Mock<IDataTypeService>();
// fixme - temp both needed by NestedContentHelper to read preValues
container.RegisterSingleton<ServiceContext>();
container.RegisterSingleton(f => dataTypeService.Object);
// mocked dataservice returns nested content preValues
dataTypeService
.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny<int>()))
.Returns((int id) =>
{
if (id == 1)
return new PreValueCollection(new Dictionary<string, PreValue>
{
{ "minItems", new PreValue("1") },
{ "maxItems", new PreValue("1") },
{ "contentTypes", new PreValue("contentN1") }
});
if (id == 2)
return new PreValueCollection(new Dictionary<string, PreValue>
{
{ "minItems", new PreValue("1") },
{ "maxItems", new PreValue("99") },
{ "contentTypes", new PreValue("contentN1") }
});
return null;
});
var publishedModelFactory = new Mock<IPublishedModelFactory>();
// fixme - temp needed by PublishedPropertyType
container.RegisterSingleton(f => publishedModelFactory.Object);
// mocked model factory returns model type
publishedModelFactory
.Setup(x => x.ModelTypeMap)
.Returns(new Dictionary<string, Type>
{
{ "contentN1", typeof (TestModel) }
});
// mocked model factory creates models
publishedModelFactory
.Setup(x => x.CreateModel(It.IsAny<IPublishedElement>()))
.Returns((IPublishedElement element) =>
{
if (element.ContentType.Alias.InvariantEquals("contentN1"))
return new TestModel(element);
return element;
});
var contentCache = new Mock<IPublishedContentCache>();
var facade = new Mock<IFacade>();
// mocked facade returns a content cache
facade
.Setup(x => x.ContentCache)
.Returns(contentCache.Object);
var facadeAccessor = new Mock<IFacadeAccessor>();
//container.RegisterSingleton(f => facadeAccessor.Object);
// mocked facade accessor returns a facade
facadeAccessor
.Setup(x => x.Facade)
.Returns(facade.Object);
var facadeService = new Mock<IFacadeService>();
//container.RegisterSingleton(f => facadeService.Object);
// mocked facade service creates element properties
facadeService
.Setup(x => x.CreateElementProperty(It.IsAny<PublishedPropertyType>(), It.IsAny<IPublishedElement>(), It.IsAny<bool>(), It.IsAny<PropertyCacheLevel>(), It.IsAny<object>()))
.Returns((PublishedPropertyType propertyType, IPublishedElement element, bool preview, PropertyCacheLevel referenceCacheLevel, object source)
=> new TestPublishedProperty(propertyType, element, preview, referenceCacheLevel, source));
var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[]
{
new NestedContentSingleValueConverter(facadeAccessor.Object, facadeService.Object, publishedModelFactory.Object, proflog),
new NestedContentManyValueConverter(facadeAccessor.Object, facadeService.Object, publishedModelFactory.Object, proflog),
});
var propertyType1 = new PublishedPropertyType("property1", 1, Constants.PropertyEditors.NestedContentAlias, converters);
var propertyType2 = new PublishedPropertyType("property2", 2, Constants.PropertyEditors.NestedContentAlias, converters);
var propertyTypeN1 = new PublishedPropertyType("propertyN1", Constants.PropertyEditors.TextboxAlias, converters);
var contentType1 = new PublishedContentType(1, "content1", new[] { propertyType1 });
var contentType2 = new PublishedContentType(2, "content2", new[] { propertyType2 });
var contentTypeN1 = new PublishedContentType(2, "contentN1", new[] { propertyTypeN1 });
// mocked content cache returns content types
contentCache
.Setup(x => x.GetContentType(It.IsAny<string>()))
.Returns((string alias) =>
{
if (alias.InvariantEquals("contentN1")) return contentTypeN1;
return null;
});
return (contentType1, contentType2);
}
[Test]
public void SingleNestedTest()
{
(var contentType1, _) = CreateContentTypes();
// nested single converter returns the proper value clr type TestModel, and cache level
Assert.AreEqual(typeof (TestModel), contentType1.GetPropertyType("property1").ClrType);
Assert.AreEqual(PropertyCacheLevel.Content, contentType1.GetPropertyType("property1").CacheLevel);
var key = Guid.NewGuid();
var keyA = Guid.NewGuid();
var content = new TestPublishedContent(contentType1, key, new[]
{
new TestPublishedProperty(contentType1.GetPropertyType("property1"), $@"[
{{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }}
]")
});
var value = content.Value("property1");
// nested single converter returns proper TestModel value
Assert.IsInstanceOf<TestModel>(value);
var valueM = (TestModel) value;
Assert.AreEqual("foo", valueM.PropValue);
Assert.AreEqual(keyA, valueM.Key);
}
[Test]
public void ManyNestedTest()
{
(_, var contentType2) = CreateContentTypes();
// nested many converter returns the proper value clr type IEnumerable<TestModel>, and cache level
Assert.AreEqual(typeof (IEnumerable<TestModel>), contentType2.GetPropertyType("property2").ClrType);
Assert.AreEqual(PropertyCacheLevel.Content, contentType2.GetPropertyType("property2").CacheLevel);
var key = Guid.NewGuid();
var keyA = Guid.NewGuid();
var keyB = Guid.NewGuid();
var content = new TestPublishedContent(contentType2, key, new[]
{
new TestPublishedProperty(contentType2.GetPropertyType("property2"), $@"[
{{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }},
{{ ""key"": ""{keyB}"", ""propertyN1"": ""bar"", ""ncContentTypeAlias"": ""contentN1"" }}
]")
});
var value = content.Value("property2");
// nested many converter returns proper IEnumerable<TestModel> value
Assert.IsInstanceOf<IEnumerable<IPublishedElement>>(value);
Assert.IsInstanceOf<IEnumerable<TestModel>>(value);
var valueM = ((IEnumerable<TestModel>) value).ToArray();
Assert.AreEqual("foo", valueM[0].PropValue);
Assert.AreEqual(keyA, valueM[0].Key);
Assert.AreEqual("bar", valueM[1].PropValue);
Assert.AreEqual(keyB, valueM[1].Key);
}
// note: this class needs to be public enough, for the converters to be able to instanciate it
public class TestModel : PublishedElementModel
{
public TestModel(IPublishedElement content)
: base(content)
{ }
public string PropValue => this.Value<string>("propertyN1");
}
class TestPublishedProperty : PublishedPropertyBase
{
private readonly bool _preview;
private IPublishedElement _owner;
public TestPublishedProperty(PublishedPropertyType propertyType, object source)
: base(propertyType, PropertyCacheLevel.Content) // initial reference cache level always is .Content
{
SourceValue = source;
HasValue = source != null && (!(source is string ssource) || !string.IsNullOrWhiteSpace(ssource));
}
public TestPublishedProperty(PublishedPropertyType propertyType, IPublishedElement element, bool preview, PropertyCacheLevel referenceCacheLevel, object source)
: base(propertyType, referenceCacheLevel)
{
SourceValue = source;
HasValue = source != null && (!(source is string ssource) || !string.IsNullOrWhiteSpace(ssource));
_owner = element;
_preview = preview;
}
private object InterValue => PropertyType.ConvertSourceToInter(null, SourceValue, false);
internal void SetOwner(IPublishedElement owner)
{
_owner = owner;
}
public override bool HasValue { get; }
public override object SourceValue { get; }
public override object Value => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview);
public override object XPathValue => throw new WontImplementException();
}
class TestPublishedContent : PublishedContentBase
{
public TestPublishedContent(PublishedContentType contentType, Guid key, IEnumerable<TestPublishedProperty> properties)
{
ContentType = contentType;
Key = key;
var propertiesA = properties.ToArray();
Properties = propertiesA;
foreach (var property in propertiesA)
property.SetOwner(this);
}
// ReSharper disable UnassignedGetOnlyAutoProperty
public override PublishedItemType ItemType { get; }
public override bool IsDraft { get; }
public override IPublishedContent Parent { get; }
public override IEnumerable<IPublishedContent> Children { get; }
public override PublishedContentType ContentType { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty
// ReSharper disable UnassignedGetOnlyAutoProperty
public override int Id { get; }
public override int TemplateId { get; }
public override int SortOrder { get; }
public override string Name { get; }
public override string UrlName { get; }
public override string DocumentTypeAlias { get; }
public override int DocumentTypeId { get; }
public override string WriterName { get; }
public override string CreatorName { get; }
public override int WriterId { get; }
public override int CreatorId { get; }
public override string Path { get; }
public override DateTime CreateDate { get; }
public override DateTime UpdateDate { get; }
public override Guid Version { get; }
public override int Level { get; }
public override Guid Key { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty
public override IEnumerable<IPublishedProperty> Properties { get; }
public override IPublishedProperty GetProperty(string alias)
{
return Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
}
}
}

View File

@@ -8,7 +8,6 @@ namespace Umbraco.Tests.FrontEnd
[TestFixture]
public class UmbracoHelperTests
{
[Test]
public void Truncate_Simple()
{

View File

@@ -189,7 +189,7 @@ namespace Umbraco.Tests.PublishedContent
.Append<SimpleConverter3A>()
.Append<SimpleConverter3B>();
IPublishedContentModelFactory factory = new PublishedContentModelFactory(new[]
IPublishedModelFactory factory = new PublishedModelFactory(new[]
{
typeof(TestSetModel1), typeof(TestSetModel2),
typeof(TestContentModel1), typeof(TestContentModel2),
@@ -405,7 +405,7 @@ namespace Umbraco.Tests.PublishedContent
var facadeServiceMock = new Mock<IFacadeService>();
facadeServiceMock
.Setup(x => x.CreateSetProperty(It.IsAny<PublishedPropertyType>(), It.IsAny<IPublishedElement>(), It.IsAny<bool>(), It.IsAny<PropertyCacheLevel>(), It.IsAny<object>()))
.Setup(x => x.CreateElementProperty(It.IsAny<PublishedPropertyType>(), It.IsAny<IPublishedElement>(), It.IsAny<bool>(), It.IsAny<PropertyCacheLevel>(), It.IsAny<object>()))
.Returns<PublishedPropertyType, IPublishedElement, bool, PropertyCacheLevel, object>((propertyType, set, previewing, refCacheLevel, value) =>
{
// ReSharper disable AccessToModifiedClosure

View File

@@ -142,8 +142,9 @@ namespace Umbraco.Tests.PublishedContent
Properties = new Collection<IPublishedProperty>(
new List<IPublishedProperty>()
{
new PropertyResult("property1", "value" + indexVals, PropertyResultType.UserProperty),
new PropertyResult("property2", "value" + (indexVals + 1), PropertyResultType.UserProperty)
// PropertyResult is gone, this should be done differently
//new PropertyResult("property1", "value" + indexVals, PropertyResultType.UserProperty),
//new PropertyResult("property2", "value" + (indexVals + 1), PropertyResultType.UserProperty)
}),
Children = new List<IPublishedContent>()
};
@@ -156,15 +157,17 @@ namespace Umbraco.Tests.PublishedContent
GetContent(false, indexVals + 9)
};
}
if (!createChildren)
{
//create additional columns, used to test the different columns for child nodes
((Collection<IPublishedProperty>)d.Properties).Add(new PropertyResult("property4", "value" + (indexVals + 2), PropertyResultType.UserProperty));
}
else
{
((Collection<IPublishedProperty>)d.Properties).Add(new PropertyResult("property3", "value" + (indexVals + 2), PropertyResultType.UserProperty));
}
// PropertyResult is gone, this should be done differently
//if (!createChildren)
//{
// //create additional columns, used to test the different columns for child nodes
// ((Collection<IPublishedProperty>)d.Properties).Add(new PropertyResult("property4", "value" + (indexVals + 2), PropertyResultType.UserProperty));
//}
//else
//{
// ((Collection<IPublishedProperty>)d.Properties).Add(new PropertyResult("property3", "value" + (indexVals + 2), PropertyResultType.UserProperty));
//}
return d;
}

View File

@@ -35,7 +35,7 @@ namespace Umbraco.Tests.PublishedContent
{
base.Compose();
Container.RegisterSingleton<IPublishedContentModelFactory>(f => new PublishedContentModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
Container.RegisterSingleton<IPublishedModelFactory>(f => new PublishedModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
}
protected override TypeLoader CreatePluginManager(IServiceFactory f)

View File

@@ -54,7 +54,7 @@ namespace Umbraco.Tests.PublishedContent
{
base.Compose();
Container.RegisterSingleton<IPublishedContentModelFactory>(f => new PublishedContentModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
Container.RegisterSingleton<IPublishedModelFactory>(f => new PublishedModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
}
protected override TypeLoader CreatePluginManager(IServiceFactory f)

View File

@@ -273,7 +273,7 @@ namespace Umbraco.Tests.Testing
Container.RegisterSingleton<IFileSystem>(factory => new PhysicalFileSystem("MasterPages", "/masterpages"), "MasterpageFileSystem");
// no factory (noop)
Container.RegisterSingleton<IPublishedContentModelFactory, NoopPublishedContentModelFactory>();
Container.RegisterSingleton<IPublishedModelFactory, NoopPublishedModelFactory>();
// register application stuff (database factory & context, services...)
Container.RegisterCollectionBuilder<MapperCollectionBuilder>()

View File

@@ -168,6 +168,9 @@
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Text.Encoding" />
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@@ -218,6 +221,7 @@
<Compile Include="CoreThings\CallContextTests.cs" />
<Compile Include="Components\ComponentTests.cs" />
<Compile Include="CoreXml\RenamedRootNavigatorTests.cs" />
<Compile Include="Facade\NestedContentTests.cs" />
<Compile Include="Misc\DateTimeExtensionsTests.cs" />
<Compile Include="Misc\HashGeneratorTests.cs" />
<Compile Include="IO\ShadowFileSystemTests.cs" />

View File

@@ -35,4 +35,5 @@
<package id="Semver" version="2.0.4" targetFramework="net461" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net461" />
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net461" />
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
</packages>

View File

@@ -233,7 +233,7 @@ namespace Umbraco.Web.Composing
internal static PropertyValueConverterCollection PropertyValueConverters => CoreCurrent.PropertyValueConverters;
internal static IPublishedContentModelFactory PublishedContentModelFactory => CoreCurrent.PublishedContentModelFactory;
internal static IPublishedModelFactory PublishedModelFactory => CoreCurrent.PublishedModelFactory;
public static IServerMessenger ServerMessenger => CoreCurrent.ServerMessenger;

View File

@@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Models
{
public class DetachedPublishedContent : PublishedContentBase
{
private readonly PublishedContentType _contentType;
private readonly IEnumerable<IPublishedProperty> _properties;
private readonly IPublishedContent _containerNode;
public DetachedPublishedContent(
Guid key,
string name,
PublishedContentType contentType,
IEnumerable<IPublishedProperty> properties,
IPublishedContent containerNode = null,
int sortOrder = 0,
bool isPreviewing = false)
{
Key = key;
Name = name;
_contentType = contentType;
_properties = properties;
SortOrder = sortOrder;
IsDraft = isPreviewing;
_containerNode = containerNode;
}
public override Guid Key { get; }
public override int Id => 0;
public override string Name { get; }
public override bool IsDraft { get; }
public override PublishedItemType ItemType => PublishedItemType.Content;
public override PublishedContentType ContentType => _contentType;
public override string DocumentTypeAlias => _contentType.Alias;
public override int DocumentTypeId => _contentType.Id;
public override IEnumerable<IPublishedProperty> Properties => _properties.ToArray();
public override IPublishedProperty GetProperty(string alias)
=> _properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
public override IPublishedProperty GetProperty(string alias, bool recurse)
{
if (recurse)
throw new NotSupportedException();
return GetProperty(alias);
}
public override IPublishedContent Parent => null;
public override IEnumerable<IPublishedContent> Children => Enumerable.Empty<IPublishedContent>();
public override int TemplateId => 0;
public override int SortOrder { get; }
public override string UrlName => null;
public override string WriterName => _containerNode?.WriterName;
public override string CreatorName => _containerNode?.CreatorName;
public override int WriterId => _containerNode?.WriterId ?? 0;
public override int CreatorId => _containerNode?.CreatorId ?? 0;
public override string Path => null;
public override DateTime CreateDate => _containerNode?.CreateDate ?? DateTime.MinValue;
public override DateTime UpdateDate => _containerNode?.UpdateDate ?? DateTime.MinValue;
public override Guid Version => _containerNode?.Version ?? Guid.Empty;
public override int Level => 0;
}
}

View File

@@ -1,44 +0,0 @@
using System;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.Models
{
internal class DetachedPublishedProperty : IPublishedProperty
{
private readonly PublishedPropertyType _propertyType;
private readonly object _sourceValue;
private readonly Lazy<object> _objectValue;
private readonly Lazy<object> _xpathValue;
private readonly bool _isPreview;
public DetachedPublishedProperty(PublishedPropertyType propertyType, object value)
: this(propertyType, value, false)
{ }
public DetachedPublishedProperty(PublishedPropertyType propertyType, object value, bool isPreview)
{
_propertyType = propertyType;
_isPreview = isPreview;
_sourceValue = value;
IPublishedElement publishedElement = null; // fixme!! nested content needs complete refactoring!
var interValue = new Lazy<object>(() => _propertyType.ConvertSourceToInter(publishedElement, _sourceValue, _isPreview));
_objectValue = new Lazy<object>(() => _propertyType.ConvertInterToObject(publishedElement, PropertyCacheLevel.None, interValue.Value, _isPreview));
_xpathValue = new Lazy<object>(() => _propertyType.ConvertInterToXPath(publishedElement, PropertyCacheLevel.None, interValue.Value, _isPreview));
}
public string PropertyTypeAlias => _propertyType.PropertyTypeAlias;
public bool HasValue => SourceValue != null && SourceValue.ToString().Trim().Length > 0;
public object SourceValue => _sourceValue;
public object Value => _objectValue.Value;
public object XPathValue => _xpathValue.Value;
}
}

View File

@@ -25,107 +25,17 @@ namespace Umbraco.Web.PropertyEditors
string.Concat(CacheKeyPrefix, id));
}
public static string GetContentTypeAliasFromItem(JObject item)
public static string GetElementTypeAlias(JObject item)
{
var contentTypeAliasProperty = item[NestedContentPropertyEditor.ContentTypeAliasPropertyKey];
if (contentTypeAliasProperty == null)
{
return null;
}
return contentTypeAliasProperty.ToObject<string>();
return item[NestedContentPropertyEditor.ContentTypeAliasPropertyKey]?.ToObject<string>();
}
public static IContentType GetContentTypeFromItem(JObject item)
public static IContentType GetElementType(JObject item)
{
var contentTypeAlias = GetContentTypeAliasFromItem(item);
if (string.IsNullOrEmpty(contentTypeAlias))
{
return null;
}
return Current.Services.ContentTypeService.Get(contentTypeAlias);
var contentTypeAlias = GetElementTypeAlias(item);
return string.IsNullOrEmpty(contentTypeAlias)
? null
: Current.Services.ContentTypeService.Get(contentTypeAlias);
}
#region Conversion from v0.1.1 data formats
public static void ConvertItemValueFromV011(JObject item, int dtdId, ref PreValueCollection preValues)
{
var contentTypeAlias = GetContentTypeAliasFromItem(item);
if (contentTypeAlias != null)
{
// the item is already in >v0.1.1 format
return;
}
// old style (v0.1.1) data, let's attempt a conversion
// - get the prevalues (if they're not loaded already)
preValues = preValues ?? GetPreValuesCollectionByDataTypeId(dtdId);
// - convert the prevalues (if necessary)
ConvertPreValueCollectionFromV011(preValues);
// - get the content types prevalue as JArray
var preValuesAsDictionary = preValues.PreValuesAsDictionary.ToDictionary(x => x.Key, x => x.Value.Value);
if (!preValuesAsDictionary.ContainsKey(ContentTypesPreValueKey) || string.IsNullOrEmpty(preValuesAsDictionary[ContentTypesPreValueKey]) != false)
{
return;
}
var preValueContentTypes = JArray.Parse(preValuesAsDictionary[ContentTypesPreValueKey]);
if (preValueContentTypes.Any())
{
// the only thing we can really do is assume that the item is the first available content type
item[NestedContentPropertyEditor.ContentTypeAliasPropertyKey] = preValueContentTypes.First().Value<string>("ncAlias");
}
}
public static void ConvertPreValueCollectionFromV011(PreValueCollection preValueCollection)
{
if (preValueCollection == null)
{
return;
}
var persistedPreValuesAsDictionary = preValueCollection.PreValuesAsDictionary.ToDictionary(x => x.Key, x => x.Value.Value);
// do we have a "docTypeGuid" prevalue and no "contentTypes" prevalue?
if (persistedPreValuesAsDictionary.ContainsKey("docTypeGuid") == false || persistedPreValuesAsDictionary.ContainsKey(ContentTypesPreValueKey))
{
// the prevalues are already in >v0.1.1 format
return;
}
// attempt to parse the doc type guid
Guid guid;
if (Guid.TryParse(persistedPreValuesAsDictionary["docTypeGuid"], out guid) == false)
{
// this shouldn't happen... but just in case.
return;
}
// find the content type
var contentType = Current.Services.ContentTypeService.Get(guid);
if (contentType == null)
{
return;
}
// add a prevalue in the format expected by the new (>0.1.1) content type picker/configurator
preValueCollection.PreValuesAsDictionary[ContentTypesPreValueKey] = new PreValue(
string.Format(@"[{{""ncAlias"": ""{0}"", ""ncTabAlias"": ""{1}"", ""nameTemplate"": ""{2}"", }}]",
contentType.Alias,
persistedPreValuesAsDictionary["tabAlias"],
persistedPreValuesAsDictionary["nameTemplate"]
)
);
}
private static string ContentTypesPreValueKey
{
get { return NestedContentPropertyEditor.NestedContentPreValueEditor.ContentTypesPreValueKey; }
}
#endregion
}
}

View File

@@ -76,14 +76,6 @@ namespace Umbraco.Web.PropertyEditors
[PreValueField("hideLabel", "Hide Label", "boolean", Description = "Set whether to hide the editor label and have the list take up the full width of the editor window.")]
public string HideLabel { get; set; }
public override IDictionary<string, object> ConvertDbToEditor(IDictionary<string, object> defaultPreVals, PreValueCollection persistedPreVals)
{
// re-format old style (v0.1.1) pre values if necessary
NestedContentHelper.ConvertPreValueCollectionFromV011(persistedPreVals);
return base.ConvertDbToEditor(defaultPreVals, persistedPreVals);
}
}
#endregion
@@ -142,10 +134,7 @@ namespace Umbraco.Web.PropertyEditors
var o = value[i];
var propValues = ((JObject)o);
// convert from old style (v0.1.1) data format if necessary
NestedContentHelper.ConvertItemValueFromV011(propValues, propertyType.DataTypeDefinitionId, ref preValues);
var contentType = NestedContentHelper.GetContentTypeFromItem(propValues);
var contentType = NestedContentHelper.GetElementType(propValues);
if (contentType == null)
{
continue;
@@ -217,10 +206,7 @@ namespace Umbraco.Web.PropertyEditors
var o = value[i];
var propValues = ((JObject)o);
// convert from old style (v0.1.1) data format if necessary
NestedContentHelper.ConvertItemValueFromV011(propValues, propertyType.DataTypeDefinitionId, ref preValues);
var contentType = NestedContentHelper.GetContentTypeFromItem(propValues);
var contentType = NestedContentHelper.GetElementType(propValues);
if (contentType == null)
{
continue;
@@ -298,7 +284,7 @@ namespace Umbraco.Web.PropertyEditors
var o = value[i];
var propValues = ((JObject)o);
var contentType = NestedContentHelper.GetContentTypeFromItem(propValues);
var contentType = NestedContentHelper.GetElementType(propValues);
if (contentType == null)
{
continue;
@@ -368,7 +354,7 @@ namespace Umbraco.Web.PropertyEditors
var o = value[i];
var propValues = (JObject) o;
var contentType = NestedContentHelper.GetContentTypeFromItem(propValues);
var contentType = NestedContentHelper.GetElementType(propValues);
if (contentType == null)
{
continue;

View File

@@ -1,41 +1,105 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
public class NestedContentManyValueConverter : PropertyValueConverterBase
/// <inheritdoc />
/// <summary>
/// Provides an implementation for <see cref="T:Umbraco.Core.PropertyEditors.IPropertyValueConverter" /> for nested content.
/// </summary>
public class NestedContentManyValueConverter : NestedContentValueConverterBase
{
private readonly ILogger _logger;
private readonly ConcurrentDictionary<Type, Func<object>> _listCtors = new ConcurrentDictionary<Type, Func<object>>();
private readonly ProfilingLogger _proflog;
public NestedContentManyValueConverter(ILogger logger)
/// <summary>
/// Initializes a new instance of the <see cref="NestedContentManyValueConverter"/> class.
/// </summary>
public NestedContentManyValueConverter(IFacadeAccessor facadeAccessor, IFacadeService facadeService, IPublishedModelFactory publishedModelFactory, ProfilingLogger proflog)
: base(facadeAccessor, facadeService, publishedModelFactory)
{
_logger = logger;
_proflog = proflog;
}
/// <inheritdoc />
public override bool IsConverter(PublishedPropertyType propertyType)
=> propertyType.IsNestedContentProperty() && !propertyType.IsSingleNestedContentProperty();
=> IsNestedMany(propertyType);
/// <inheritdoc />
public override Type GetPropertyValueType(PublishedPropertyType propertyType)
=> typeof(IEnumerable<IPublishedContent>);
{
var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId);
var contentTypes = preValueCollection.PreValuesAsDictionary["contentTypes"].Value;
return contentTypes.Contains(",")
? typeof (IEnumerable<IPublishedElement>)
: typeof (IEnumerable<>).MakeGenericType(ModelType.For(contentTypes));
}
/// <inheritdoc />
public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType)
=> PropertyCacheLevel.Content;
/// <inheritdoc />
public override object ConvertSourceToInter(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
{
try
{
return propertyType.ConvertPropertyToNestedContent(source, preview);
}
catch (Exception e)
{
_logger.Error<NestedContentManyValueConverter>("Error converting value", e);
}
return source?.ToString();
}
return null;
/// <inheritdoc />
public override object ConvertInterToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
using (_proflog.DebugDuration<PublishedPropertyType>($"ConvertPropertyToNestedContent ({propertyType.DataTypeId})"))
{
var value = (string)inter;
if (string.IsNullOrWhiteSpace(value)) return null;
var objects = JsonConvert.DeserializeObject<List<JObject>>(value);
if (objects.Count == 0)
return Enumerable.Empty<IPublishedElement>();
// fixme do NOT do it here!
var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId);
var contentTypes = preValueCollection.PreValuesAsDictionary["contentTypes"].Value;
IList elements;
if (contentTypes.Contains(","))
{
elements = new List<IPublishedElement>();
}
else if (PublishedModelFactory.ModelTypeMap.TryGetValue(contentTypes, out var type))
{
var ctor = _listCtors.GetOrAdd(type, t =>
{
var listType = typeof(List<>).MakeGenericType(t);
return ReflectionUtilities.GetCtor(listType);
});
elements = (IList) ctor();
}
else
{
// should we throw instead?
elements = new List<IPublishedElement>();
}
foreach (var sourceObject in objects)
{
var element = ConvertToElement(sourceObject, referenceCacheLevel, preview);
if (element != null)
elements.Add(element);
}
return elements;
}
}
}
}

View File

@@ -1,133 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Models;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
internal static class NestedContentPublishedPropertyTypeExtensions
{
public static bool IsNestedContentProperty(this PublishedPropertyType publishedProperty)
{
return publishedProperty.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.NestedContentAlias);
}
public static bool IsSingleNestedContentProperty(this PublishedPropertyType publishedProperty)
{
if (!publishedProperty.IsNestedContentProperty())
{
return false;
}
var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(publishedProperty.DataTypeId);
var preValueDictionary = preValueCollection.PreValuesAsDictionary.ToDictionary(x => x.Key, x => x.Value.Value);
return preValueDictionary.ContainsKey("minItems") &&
int.TryParse(preValueDictionary["minItems"], out var minItems) && minItems == 1
&& preValueDictionary.ContainsKey("maxItems") &&
int.TryParse(preValueDictionary["maxItems"], out var maxItems) && maxItems == 1;
}
public static object ConvertPropertyToNestedContent(this PublishedPropertyType propertyType, object source, bool preview)
{
using (Current.ProfilingLogger.DebugDuration<PublishedPropertyType>($"ConvertPropertyToNestedContent ({propertyType.DataTypeId})"))
{
if (source != null && !source.ToString().IsNullOrWhiteSpace())
{
var rawValue = JsonConvert.DeserializeObject<List<object>>(source.ToString());
var processedValue = new List<IPublishedContent>();
var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId);
var preValueDictionary = preValueCollection.PreValuesAsDictionary.ToDictionary(x => x.Key, x => x.Value.Value);
for (var i = 0; i < rawValue.Count; i++)
{
var item = (JObject)rawValue[i];
// Convert from old style (v.0.1.1) data format if necessary
// - Please note: This call has virtually no impact on rendering performance for new style (>v0.1.1).
// Even so, this should be removed eventually, when it's safe to assume that there is
// no longer any need for conversion.
NestedContentHelper.ConvertItemValueFromV011(item, propertyType.DataTypeId, ref preValueCollection);
var contentTypeAlias = NestedContentHelper.GetContentTypeAliasFromItem(item);
if (string.IsNullOrEmpty(contentTypeAlias))
{
continue;
}
var publishedContentType = Composing.Current.Facade.ContentCache.GetContentType(contentTypeAlias);
if (publishedContentType == null)
{
continue;
}
var propValues = item.ToObject<Dictionary<string, object>>();
var properties = new List<IPublishedProperty>();
foreach (var jProp in propValues)
{
var propType = publishedContentType.GetPropertyType(jProp.Key);
if (propType != null)
{
properties.Add(new DetachedPublishedProperty(propType, jProp.Value, preview));
}
}
// Parse out the name manually
object nameObj = null;
if (propValues.TryGetValue("name", out nameObj))
{
// Do nothing, we just want to parse out the name if we can
}
object keyObj;
var key = Guid.Empty;
if (propValues.TryGetValue("key", out keyObj))
{
key = Guid.Parse(keyObj.ToString());
}
// Get the current request node we are embedded in
var pcr = UmbracoContext.Current == null ? null : UmbracoContext.Current.PublishedContentRequest;
var containerNode = pcr != null && pcr.HasPublishedContent ? pcr.PublishedContent : null;
// Create the model based on our implementation of IPublishedContent
IPublishedContent content = new DetachedPublishedContent(
key,
nameObj == null ? null : nameObj.ToString(),
publishedContentType,
properties.ToArray(),
containerNode,
i,
preview);
if (Current.PublishedContentModelFactory != null)
{
// Let the current model factory create a typed model to wrap our model
content = (IPublishedContent) Current.PublishedContentModelFactory.CreateModel(content);
}
// Add the (typed) model as a result
processedValue.Add(content);
}
if (propertyType.IsSingleNestedContentProperty())
{
return processedValue.FirstOrDefault();
}
return processedValue;
}
}
return null;
}
}
}

View File

@@ -1,40 +1,71 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
public class NestedContentSingleValueConverter : PropertyValueConverterBase
/// <inheritdoc />
/// <summary>
/// Provides an implementation for <see cref="T:Umbraco.Core.PropertyEditors.IPropertyValueConverter" /> for nested content.
/// </summary>
public class NestedContentSingleValueConverter : NestedContentValueConverterBase
{
private readonly ILogger _logger;
private readonly ProfilingLogger _proflog;
public NestedContentSingleValueConverter(ILogger logger)
/// <summary>
/// Initializes a new instance of the <see cref="NestedContentSingleValueConverter"/> class.
/// </summary>
public NestedContentSingleValueConverter(IFacadeAccessor facadeAccessor, IFacadeService facadeService, IPublishedModelFactory publishedModelFactory, ProfilingLogger proflog)
: base(facadeAccessor, facadeService, publishedModelFactory)
{
_logger = logger;
_proflog = proflog;
}
/// <inheritdoc />
public override bool IsConverter(PublishedPropertyType propertyType)
=> propertyType.IsSingleNestedContentProperty();
=> IsNestedSingle(propertyType);
/// <inheritdoc />
public override Type GetPropertyValueType(PublishedPropertyType propertyType)
=> typeof(IPublishedContent);
{
var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId);
var contentTypes = preValueCollection.PreValuesAsDictionary["contentTypes"].Value;
return contentTypes.Contains(",")
? typeof(IPublishedElement)
: ModelType.For(contentTypes);
}
/// <inheritdoc />
public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType)
=> PropertyCacheLevel.Content;
/// <inheritdoc />
public override object ConvertSourceToInter(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
{
try
{
return propertyType.ConvertPropertyToNestedContent(source, preview);
}
catch (Exception e)
{
_logger.Error<NestedContentSingleValueConverter>("Error converting value", e);
}
return source?.ToString();
}
return null;
/// <inheritdoc />
public override object ConvertInterToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
using (_proflog.DebugDuration<PublishedPropertyType>($"ConvertPropertyToNestedContent ({propertyType.DataTypeId})"))
{
var value = (string) inter;
if (string.IsNullOrWhiteSpace(value)) return null;
var objects = JsonConvert.DeserializeObject<List<JObject>>(value);
if (objects.Count == 0)
return null;
if (objects.Count > 1)
throw new InvalidOperationException();
return ConvertToElement(objects[0], referenceCacheLevel, preview);
}
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
public abstract class NestedContentValueConverterBase : PropertyValueConverterBase
{
private readonly IFacadeAccessor _facadeAccessor;
private readonly IFacadeService _facadeService;
protected NestedContentValueConverterBase(IFacadeAccessor facadeAccessor, IFacadeService facadeService, IPublishedModelFactory publishedModelFactory)
{
_facadeAccessor = facadeAccessor;
_facadeService = facadeService;
PublishedModelFactory = publishedModelFactory;
}
protected IPublishedModelFactory PublishedModelFactory { get; }
public static bool IsNested(PublishedPropertyType publishedProperty)
{
return publishedProperty.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.NestedContentAlias);
}
public static bool IsNestedSingle(PublishedPropertyType publishedProperty)
{
if (!IsNested(publishedProperty))
return false;
var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(publishedProperty.DataTypeId);
var preValueDictionary = preValueCollection.PreValuesAsDictionary;
return preValueDictionary.TryGetValue("minItems", out var minItems)
&& preValueDictionary.TryGetValue("maxItems", out var maxItems)
&& int.TryParse(minItems.Value, out var minItemsValue) && minItemsValue == 1
&& int.TryParse(maxItems.Value, out var maxItemsValue) && maxItemsValue == 1;
}
public static bool IsNestedMany(PublishedPropertyType publishedProperty)
{
return IsNested(publishedProperty) && !IsNestedSingle(publishedProperty);
}
protected IPublishedElement ConvertToElement(JObject sourceObject, PropertyCacheLevel referenceCacheLevel, bool preview)
{
var elementTypeAlias = NestedContentHelper.GetElementTypeAlias(sourceObject);
if (string.IsNullOrEmpty(elementTypeAlias))
return null;
var publishedContentType = _facadeAccessor.Facade.ContentCache.GetContentType(elementTypeAlias);
if (publishedContentType == null)
return null;
var propertyValues = sourceObject.ToObject<Dictionary<string, object>>();
if (!propertyValues.TryGetValue("key", out var keyo)
|| !Guid.TryParse(keyo.ToString(), out var key))
key = Guid.Empty;
// fixme - why does it need a facade service here?
IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, _facadeService, referenceCacheLevel);
element = PublishedModelFactory.CreateModel(element);
return element;
}
}
}

View File

@@ -28,13 +28,13 @@ namespace Umbraco.Web.PublishedCache
public virtual IPublishedElement CreateSet(PublishedContentType contentType, Guid key, Dictionary<string, object> values, bool previewing, PropertyCacheLevel referenceCacheLevel)
{
var set = new PublishedElement(contentType, key, values, previewing, this, referenceCacheLevel);
var model = Current.PublishedContentModelFactory.CreateModel(set);
var model = Current.PublishedModelFactory.CreateModel(set);
if (model == null)
throw new Exception("Factory returned null.");
return model;
}
public abstract IPublishedProperty CreateSetProperty(PublishedPropertyType propertyType, IPublishedElement set, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null);
public abstract IPublishedProperty CreateElementProperty(PublishedPropertyType propertyType, IPublishedElement element, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null);
public abstract string EnterPreview(IUser user, int contentId);
public abstract void RefreshPreview(string previewToken, int contentId);

View File

@@ -1,6 +1,4 @@
using System;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PublishedCache
{

View File

@@ -152,10 +152,10 @@ namespace Umbraco.Web.PublishedCache
#endregion
#region Property Set
#region Published Element
/// <summary>
/// Creates a property set.
/// Creates a published element.
/// </summary>
/// <param name="contentType">The content type.</param>
/// <param name="key">The set key.</param>
@@ -166,15 +166,15 @@ namespace Umbraco.Web.PublishedCache
IPublishedElement CreateSet(PublishedContentType contentType, Guid key, Dictionary<string, object> values, bool previewing, PropertyCacheLevel referenceCacheLevel);
/// <summary>
/// Creates a set property.
/// Creates a published property for a published element.
/// </summary>
/// <param name="propertyType">The property type.</param>
/// <param name="set">The set.</param>
/// <param name="element">The published element.</param>
/// <param name="previewing">A value indicating whether previewing.</param>
/// <param name="referenceCacheLevel">The reference cache level.</param>
/// <param name="sourceValue">The source value.</param>
/// <returns>A set property.</returns>
IPublishedProperty CreateSetProperty(PublishedPropertyType propertyType, IPublishedElement set, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null);
IPublishedProperty CreateElementProperty(PublishedPropertyType propertyType, IPublishedElement element, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null);
#endregion
}

View File

@@ -1534,11 +1534,11 @@ AND cmsContentNu.nodeId IS NULL
#endregion
#region Property Set
#region Published Element
public override IPublishedProperty CreateSetProperty(PublishedPropertyType propertyType, IPublishedElement set, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null)
public override IPublishedProperty CreateElementProperty(PublishedPropertyType propertyType, IPublishedElement element, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null)
{
return new PublishedElementProperty(FacadeAccessor, propertyType, set, previewing, referenceCacheLevel, sourceValue);
return new PublishedElementProperty(FacadeAccessor, propertyType, element, previewing, referenceCacheLevel, sourceValue);
}
#endregion

View File

@@ -7,12 +7,12 @@ using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PublishedCache
{
// notes:
// a property set does NOT manage any tree-like elements, neither the
// a published element does NOT manage any tree-like elements, neither the
// original NestedContent (from Lee) nor the DetachedPublishedContent POC did.
//
// at the moment we do NOT support models for sets - that would require
// an entirely new models factory + not even sure it makes sense at all since
// sets are created manually
// sets are created manually fixme yes it does!
//
internal class PublishedElement : IPublishedElement
{
@@ -31,7 +31,7 @@ namespace Umbraco.Web.PublishedCache
.Select(propertyType =>
{
values.TryGetValue(propertyType.PropertyTypeAlias, out object value);
return facadeService.CreateSetProperty(propertyType, this, previewing, referenceCacheLevel, value);
return facadeService.CreateElementProperty(propertyType, this, previewing, referenceCacheLevel, value);
})
.ToArray();
}

View File

@@ -9,7 +9,7 @@ namespace Umbraco.Web.PublishedCache
private readonly object _locko = new object();
private readonly object _sourceValue;
protected readonly IPublishedElement Set;
protected readonly IPublishedElement Set; // fixme rename
protected readonly bool IsPreviewing;
protected readonly bool IsMember;

View File

@@ -25,9 +25,9 @@ namespace Umbraco.Web.PublishedCache
_membershipUser = member;
_publishedMemberType = publishedMemberType ?? throw new ArgumentNullException(nameof(publishedMemberType));
_properties = PublishedProperty.MapProperties(_publishedMemberType.PropertyTypes, _member.Properties,
(t, v) => new RawValueProperty(t, this, v ?? string.Empty))
.ToArray();
var properties = PublishedProperty.MapProperties(_publishedMemberType.PropertyTypes, _member.Properties,
(t, v) => new RawValueProperty(t, this, v ?? string.Empty));
_properties = WithMemberProperties(properties).ToArray();
}
#region Membership provider member properties
@@ -79,52 +79,51 @@ namespace Umbraco.Web.PublishedCache
public override IPublishedProperty GetProperty(string alias)
{
switch (alias.ToLowerInvariant())
{
case "Email":
return new PropertyResult("Email", Email, PropertyResultType.CustomProperty);
case "UserName":
return new PropertyResult("UserName", UserName, PropertyResultType.CustomProperty);
case "PasswordQuestion":
return new PropertyResult("PasswordQuestion", PasswordQuestion, PropertyResultType.CustomProperty);
case "Comments":
return new PropertyResult("Comments", Email, PropertyResultType.CustomProperty);
case "IsApproved":
return new PropertyResult("IsApproved", IsApproved, PropertyResultType.CustomProperty);
case "IsLockedOut":
return new PropertyResult("IsLockedOut", IsLockedOut, PropertyResultType.CustomProperty);
case "LastLockoutDate":
return new PropertyResult("LastLockoutDate", LastLockoutDate, PropertyResultType.CustomProperty);
case "CreateDate":
return new PropertyResult("CreateDate", CreateDate, PropertyResultType.CustomProperty);
case "LastLoginDate":
return new PropertyResult("LastLoginDate", LastLoginDate, PropertyResultType.CustomProperty);
case "LastPasswordChangeDate":
return new PropertyResult("LastPasswordChangeDate", LastPasswordChangeDate, PropertyResultType.CustomProperty);
}
return _properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
private IEnumerable<IPublishedProperty> WithMemberProperties(IEnumerable<IPublishedProperty> properties)
{
var propertiesList = properties.ToList();
var aliases = propertiesList.Select(x => x.PropertyTypeAlias).ToList();
if (!aliases.Contains("Email"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("Email"), this, Email));
if (!aliases.Contains("UserName"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("UserName"), this, UserName));
if (!aliases.Contains("PasswordQuestion"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("PasswordQuestion"), this, PasswordQuestion));
if (!aliases.Contains("Comments"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("Comments"), this, Comments));
if (!aliases.Contains("IsApproved"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("IsApproved"), this, IsApproved));
if (!aliases.Contains("IsLockedOut"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("IsLockedOut"), this, IsLockedOut));
if (!aliases.Contains("LastLockoutDate"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("LastLockoutDate"), this, LastLockoutDate));
if (!aliases.Contains("CreateDate"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("CreateDate"), this, CreateDate));
if (!aliases.Contains("LastLoginDate"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("LastLoginDate"), this, LastLoginDate));
if (!aliases.Contains("LastPasswordChangeDate"))
propertiesList.Add(new RawValueProperty(ContentType.GetPropertyType("LastPasswordChangeDate"), this, LastPasswordChangeDate));
return propertiesList;
}
public override PublishedContentType ContentType => _publishedMemberType;
public override int Id => _member.Id;
public override Guid Key => _member.Key;
public override int TemplateId
{
get { throw new NotSupportedException(); }
}
public override int TemplateId => throw new NotSupportedException();
public override int SortOrder => 0;
public override string Name => _member.Name;
public override string UrlName
{
get { throw new NotSupportedException(); }
}
public override string UrlName => throw new NotSupportedException();
public override string DocumentTypeAlias => _member.ContentTypeAlias;

View File

@@ -1,45 +0,0 @@
using System;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PublishedCache
{
/// <summary>
/// A published property base that uses a raw object value.
/// </summary>
/// <remarks>Conversions results are stored within the property and will not
/// be refreshed, so this class is not suitable for cached properties.</remarks>
internal class RawValueProperty : PublishedPropertyBase
{
private readonly object _dbVal; //the value in the db
private readonly Lazy<object> _objectValue;
private readonly Lazy<object> _xpathValue;
public override object SourceValue => _dbVal;
public override bool HasValue => _dbVal != null && _dbVal.ToString().Trim().Length > 0;
public override object Value => _objectValue.Value;
public override object XPathValue => _xpathValue.Value;
// note: propertyData cannot be null
public RawValueProperty(PublishedPropertyType propertyType, IPublishedContent content, object propertyData, bool isPreviewing = false)
: this(propertyType, content, isPreviewing)
{
_dbVal = propertyData ?? throw new ArgumentNullException(nameof(propertyData));
}
// note: maintaining two ctors to make sure we understand what we do when calling them
public RawValueProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing = false)
: base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored
{
_dbVal = null;
var isPreviewing1 = isPreviewing;
var sourceValue = new Lazy<object>(() => PropertyType.ConvertSourceToInter(content, _dbVal, isPreviewing1));
_objectValue = new Lazy<object>(() => PropertyType.ConvertInterToObject(content, PropertyCacheLevel.Unknown, sourceValue.Value, isPreviewing1));
_xpathValue = new Lazy<object>(() => PropertyType.ConvertInterToXPath(content, PropertyCacheLevel.Unknown, sourceValue.Value, isPreviewing1));
}
}
}

View File

@@ -240,7 +240,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
#region Property Set
public override IPublishedProperty CreateSetProperty(PublishedPropertyType propertyType, IPublishedElement set, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null)
public override IPublishedProperty CreateElementProperty(PublishedPropertyType propertyType, IPublishedElement element, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null)
{
// fixme - ouch?
throw new NotImplementedException();

View File

@@ -695,9 +695,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
if (i.Key.InvariantStartsWith("__"))
{
// no type for that one, dunno how to convert
IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty);
_properties.Add(property);
// no type for that one, dunno how to convert, drop it
//IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty);
//_properties.Add(property);
}
else
{

View File

@@ -219,8 +219,6 @@
<Compile Include="Models\ContentEditing\UserInvite.cs" />
<Compile Include="Models\ContentEditing\UserProfile.cs" />
<Compile Include="Models\ContentEditing\UserSave.cs" />
<Compile Include="Models\DetachedPublishedContent.cs" />
<Compile Include="Models\DetachedPublishedProperty.cs" />
<Compile Include="Models\Mapping\ActionButtonsResolver.cs" />
<Compile Include="Models\Mapping\CodeFileMapperProfile.cs" />
<Compile Include="Models\Mapping\ContentTypeUdiResolver.cs" />
@@ -250,7 +248,7 @@
<Compile Include="PropertyEditors\RelatedLinks2PropertyEditor.cs" />
<Compile Include="PropertyEditors\TextOnlyValueEditor.cs" />
<Compile Include="PropertyEditors\ValueConverters\NestedContentManyValueConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\NestedContentPublishedPropertyTypeExtensions.cs" />
<Compile Include="PropertyEditors\ValueConverters\NestedContentValueConverterBase.cs" />
<Compile Include="PropertyEditors\ValueConverters\NestedContentSingleValueConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\RelatedLinksLegacyValueConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\MediaPickerValueConverter.cs" />
@@ -594,7 +592,6 @@
<Compile Include="PropertyEditors\ValueConverters\MacroContainerValueConverter.cs" />
<Compile Include="PropertyEditors\TagsDataController.cs" />
<Compile Include="PublishedCache\PublishedMember.cs" />
<Compile Include="PublishedCache\RawValueProperty.cs" />
<Compile Include="Models\ContentModelOfTContent.cs" />
<Compile Include="Mvc\JsonNetResult.cs" />
<Compile Include="Mvc\MinifyJavaScriptResultAttribute.cs" />