Facade cleanup and refactoring
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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!
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
36
src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
Normal file
36
src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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),
|
||||
|
||||
290
src/Umbraco.Tests/Facade/NestedContentTests.cs
Normal file
290
src/Umbraco.Tests/Facade/NestedContentTests.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ namespace Umbraco.Tests.FrontEnd
|
||||
[TestFixture]
|
||||
public class UmbracoHelperTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void Truncate_Simple()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>()
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PublishedCache
|
||||
{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user