Getting models and value converter setup along with tests

This commit is contained in:
Shannon
2020-01-31 15:59:27 +11:00
parent 772e46b93a
commit 292c76df0b
19 changed files with 382 additions and 42 deletions
@@ -36,6 +36,11 @@ namespace Umbraco.Core
/// </summary>
public static class Aliases
{
/// <summary>
/// CheckBox List.
/// </summary>
public const string BlockList = "Umbraco.BlockList";
/// <summary>
/// CheckBox List.
/// </summary>
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.Models.Blocks
@@ -8,9 +10,20 @@ namespace Umbraco.Core.Models.Blocks
/// </summary>
public abstract class BlockEditorModel
{
protected BlockEditorModel(IEnumerable<IPublishedElement> data)
{
Data = data ?? throw new ArgumentNullException(nameof(data));
}
public BlockEditorModel()
{
}
/// <summary>
/// The data items of the Block List editor
/// </summary>
public IEnumerable<IPublishedElement> Data { get; }
[DataMember(Name = "data")]
public IEnumerable<IPublishedElement> Data { get; set; }
}
}
@@ -1,4 +1,5 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.Models.Blocks
@@ -6,6 +7,7 @@ namespace Umbraco.Core.Models.Blocks
/// <summary>
/// Represents a layout item for the Block List editor
/// </summary>
[DataContract(Name = "blockListLayout", Namespace = "")]
public class BlockListLayoutReference : IBlockElement<IPublishedElement>
{
public BlockListLayoutReference(Udi udi, IPublishedElement settings)
@@ -14,7 +16,10 @@ namespace Umbraco.Core.Models.Blocks
Settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public Udi Udi { get; }
public IPublishedElement Settings { get; }
[DataMember(Name = "udi")]
public Udi Udi { get; set; }
[DataMember(Name = "settings")]
public IPublishedElement Settings { get; set; }
}
}
@@ -1,16 +1,30 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.Models.Blocks
{
/// <summary>
/// The strongly typed model for the Block List editor
/// </summary>
[DataContract(Name = "blockList", Namespace = "")]
public class BlockListModel : BlockEditorModel
{
public BlockListModel(IEnumerable<IPublishedElement> data, IEnumerable<BlockListLayoutReference> layout)
: base(data)
{
Layout = layout;
}
public BlockListModel()
{
}
/// <summary>
/// The layout items of the Block List editor
/// </summary>
public IEnumerable<BlockListLayoutReference> Layout { get; }
[DataMember(Name = "layout")]
public IEnumerable<BlockListLayoutReference> Layout { get; set; }
}
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Core.PropertyEditors
{
public class BlockListEditor
{
}
}
+1
View File
@@ -133,6 +133,7 @@
<Compile Include="Models\Blocks\BlockListLayoutReference.cs" />
<Compile Include="Models\Blocks\BlockListModel.cs" />
<Compile Include="Models\Blocks\IBlockElement.cs" />
<Compile Include="PropertyEditors\BlockListEditor.cs" />
<Compile Include="Runtime\IMainDomLock.cs" />
<Compile Include="Runtime\MainDomSemaphoreLock.cs" />
<Compile Include="Runtime\SqlMainDomLock.cs" />
@@ -0,0 +1,91 @@
using Moq;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.PropertyEditors;
using Umbraco.Web.PropertyEditors.ValueConverters;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Tests.PropertyEditors
{
[TestFixture]
public class BlockListPropertyValueConverterTests
{
private BlockListPropertyValueConverter Create()
{
var publishedSnapshotAccessor = Mock.Of<IPublishedSnapshotAccessor>();
var publishedModelFactory = Mock.Of<IPublishedModelFactory>();
var editor = new BlockListPropertyValueConverter(
Mock.Of<IProfilingLogger>(),
publishedModelFactory,
new BlockEditorConverter(publishedSnapshotAccessor, publishedModelFactory));
return editor;
}
[Test]
public void Is_Converter_For()
{
var editor = Create();
Assert.IsTrue(editor.IsConverter(Mock.Of<IPublishedPropertyType>(x => x.EditorAlias == Constants.PropertyEditors.Aliases.BlockList)));
Assert.IsFalse(editor.IsConverter(Mock.Of<IPublishedPropertyType>(x => x.EditorAlias == Constants.PropertyEditors.Aliases.NestedContent)));
}
[Test]
public void Get_Value_Type_Multiple()
{
var editor = Create();
var config = new BlockListConfiguration
{
ElementTypes = new[] {
new BlockListConfiguration.ElementType
{
Alias = "Test1"
},
new BlockListConfiguration.ElementType
{
Alias = "Test2"
}
}
};
var dataType = new PublishedDataType(1, "test", new Lazy<object>(() => config));
var propType = Mock.Of<IPublishedPropertyType>(x => x.DataType == dataType);
var valueType = editor.GetPropertyValueType(propType);
Assert.AreEqual(typeof(IEnumerable<IPublishedElement>), valueType);
}
[Test]
public void Get_Value_Type_Single()
{
var editor = Create();
var config = new BlockListConfiguration
{
ElementTypes = new[] {
new BlockListConfiguration.ElementType
{
Alias = "Test1"
}
}
};
var dataType = new PublishedDataType(1, "test", new Lazy<object>(() => config));
var propType = Mock.Of<IPublishedPropertyType>(x => x.DataType == dataType);
var valueType = editor.GetPropertyValueType(propType);
var modelType = typeof(IEnumerable<>).MakeGenericType(ModelType.For(config.ElementTypes[0].Alias));
// we can't compare the exact match of types because ModelType.For generates a new/different type even if the same alias is used
Assert.AreEqual(modelType.FullName, valueType.FullName);
}
}
}
@@ -119,10 +119,12 @@ namespace Umbraco.Tests.Published
.Setup(x => x.PublishedSnapshot)
.Returns(publishedSnapshot.Object);
var blockEditorConverter = new BlockEditorConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object);
var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[]
{
new NestedContentSingleValueConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object, proflog),
new NestedContentManyValueConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object, proflog),
new NestedContentSingleValueConverter(blockEditorConverter, publishedModelFactory.Object, proflog),
new NestedContentManyValueConverter(blockEditorConverter, publishedModelFactory.Object, proflog),
});
var factory = new PublishedContentTypeFactory(publishedModelFactory.Object, converters, dataTypeService);
+1
View File
@@ -145,6 +145,7 @@
<Compile Include="Persistence\Mappers\MapperTestBase.cs" />
<Compile Include="Persistence\Repositories\DocumentRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\EntityRepositoryTest.cs" />
<Compile Include="PropertyEditors\BlockListPropertyValueConverterTests.cs" />
<Compile Include="PublishedContent\NuCacheChildrenTests.cs" />
<Compile Include="PublishedContent\PublishedContentLanguageVariantTests.cs" />
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
@@ -0,0 +1,17 @@
using Umbraco.Core.Logging;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
/// <summary>
/// Abstract class for block editor based editors
/// </summary>
public abstract class BlockEditorPropertyEditor : DataEditor
{
public const string ContentTypeAliasPropertyKey = "contentTypeAlias";
public BlockEditorPropertyEditor(ILogger logger) : base(logger)
{
}
}
}
@@ -0,0 +1,24 @@
using Newtonsoft.Json;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
/// <summary>
/// The configuration object for the Block List editor
/// </summary>
public class BlockListConfiguration
{
[ConfigurationField("elementTypes", "Element Types", "views/propertyeditors/blocklist/blocklist.elementtypepicker.html", Description = "Select the Element Types to use as models for the items.")]
public ElementType[] ElementTypes { get; set; }
// TODO: Fill me in
public class ElementType
{
[JsonProperty("elementTypeAlias")]
public string Alias { get; set; }
}
}
}
@@ -0,0 +1,21 @@
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
namespace Umbraco.Web.PropertyEditors
{
[DataEditor(
Constants.PropertyEditors.Aliases.BlockList,
"Block List",
"blocklist",
Icon = "icon-list",
Group = Constants.PropertyEditors.Groups.Lists)]
public class BlockListPropertyEditor : BlockEditorPropertyEditor
{
public BlockListPropertyEditor(ILogger logger) : base(logger)
{
}
}
}
@@ -3,6 +3,7 @@ using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
/// <summary>
/// Represents the configuration for the nested content value editor.
/// </summary>
@@ -0,0 +1,45 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
public sealed class BlockEditorConverter
{
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
private readonly IPublishedModelFactory _publishedModelFactory;
public BlockEditorConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory)
{
_publishedSnapshotAccessor = publishedSnapshotAccessor;
_publishedModelFactory = publishedModelFactory;
}
public IPublishedElement ConvertToElement(
JObject sourceObject, string contentTypeAliasPropertyKey,
PropertyCacheLevel referenceCacheLevel, bool preview)
{
var elementTypeAlias = sourceObject[contentTypeAliasPropertyKey]?.ToObject<string>();
if (string.IsNullOrEmpty(elementTypeAlias))
return null;
// only convert element types - content types will cause an exception when PublishedModelFactory creates the model
var publishedContentType = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetContentType(elementTypeAlias);
if (publishedContentType == null || publishedContentType.IsElement == false)
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;
IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor);
element = _publishedModelFactory.CreateModel(element);
return element;
}
}
}
@@ -0,0 +1,107 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Blocks;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
[DefaultPropertyValueConverter(typeof(JsonValueConverter))]
public class BlockListPropertyValueConverter : PropertyValueConverterBase
{
private readonly IProfilingLogger _proflog;
private readonly IPublishedModelFactory _publishedModelFactory;
private readonly BlockEditorConverter _blockConverter;
public BlockListPropertyValueConverter(IProfilingLogger proflog, IPublishedModelFactory publishedModelFactory, BlockEditorConverter blockConverter)
{
_proflog = proflog;
_publishedModelFactory = publishedModelFactory;
_blockConverter = blockConverter;
}
/// <inheritdoc />
public override bool IsConverter(IPublishedPropertyType propertyType)
=> propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.BlockList);
/// <inheritdoc />
public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
{
var contentTypes = propertyType.DataType.ConfigurationAs<BlockListConfiguration>().ElementTypes;
return contentTypes.Length == 1
? typeof(IEnumerable<>).MakeGenericType(ModelType.For(contentTypes[0].Alias))
: typeof(IEnumerable<IPublishedElement>);
}
/// <inheritdoc />
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
=> PropertyCacheLevel.Element;
/// <inheritdoc />
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
{
return source?.ToString();
}
/// <inheritdoc />
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
using (_proflog.DebugDuration<BlockListPropertyValueConverter>($"ConvertPropertyToBlockList ({propertyType.DataType.Id})"))
{
var configuration = propertyType.DataType.ConfigurationAs<BlockListConfiguration>();
var contentTypes = configuration.ElementTypes;
var elements = contentTypes.Length == 1
? (IList<IPublishedElement>)_publishedModelFactory.CreateModelList(contentTypes[0].Alias)
: new List<IPublishedElement>();
var layout = new List<BlockListLayoutReference>();
var model = new BlockListModel(elements, layout);
var value = (string)inter;
if (string.IsNullOrWhiteSpace(value)) return model;
var objects = JsonConvert.DeserializeObject<JObject>(value);
if (objects.Count == 0) return model;
var jsonLayout = objects["layout"] as JObject;
if (jsonLayout == null) return model;
var jsonData = objects["data"] as JArray;
if (jsonData == null) return model;
var blockListLayouts = jsonLayout[Constants.PropertyEditors.Aliases.BlockList] as JArray;
if (blockListLayouts == null) return model;
foreach(var blockListLayout in blockListLayouts)
{
var settingsJson = blockListLayout["settings"] as JObject;
if (settingsJson == null) continue;
var element = _blockConverter.ConvertToElement(settingsJson, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview);
if (element == null) continue;
var layoutRef = new BlockListLayoutReference(blockListLayout.Value<Udi>("udi"), element);
layout.Add(layoutRef);
}
foreach (var data in jsonData.Cast<JObject>())
{
var element = _blockConverter.ConvertToElement(data, BlockEditorPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview);
if (element == null) continue;
elements.Add(element);
}
return model;
}
}
}
}
@@ -23,8 +23,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
/// <summary>
/// Initializes a new instance of the <see cref="NestedContentManyValueConverter"/> class.
/// </summary>
public NestedContentManyValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog)
: base(publishedSnapshotAccessor, publishedModelFactory)
public NestedContentManyValueConverter(BlockEditorConverter blockEditorConverter, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog)
: base(blockEditorConverter, publishedModelFactory)
{
_proflog = proflog;
}
@@ -71,7 +71,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
foreach (var sourceObject in objects)
{
var element = ConvertToElement(sourceObject, referenceCacheLevel, preview);
var element = BlockEditorConverter.ConvertToElement(sourceObject, NestedContentPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview);
if (element != null)
elements.Add(element);
}
@@ -22,8 +22,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
/// <summary>
/// Initializes a new instance of the <see cref="NestedContentSingleValueConverter"/> class.
/// </summary>
public NestedContentSingleValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog)
: base(publishedSnapshotAccessor, publishedModelFactory)
public NestedContentSingleValueConverter(BlockEditorConverter blockEditorConverter, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog)
: base(blockEditorConverter, publishedModelFactory)
{
_proflog = proflog;
}
@@ -65,7 +65,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
if (objects.Count > 1)
throw new InvalidOperationException();
return ConvertToElement(objects[0], referenceCacheLevel, preview);
return BlockEditorConverter.ConvertToElement(objects[0], NestedContentPropertyEditor.ContentTypeAliasPropertyKey, referenceCacheLevel, preview);
}
}
}
@@ -1,23 +1,19 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
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 IPublishedSnapshotAccessor _publishedSnapshotAccessor;
protected NestedContentValueConverterBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory)
protected NestedContentValueConverterBase(BlockEditorConverter blockEditorConverter, IPublishedModelFactory publishedModelFactory)
{
_publishedSnapshotAccessor = publishedSnapshotAccessor;
BlockEditorConverter = blockEditorConverter;
PublishedModelFactory = publishedModelFactory;
}
protected BlockEditorConverter BlockEditorConverter { get; }
protected IPublishedModelFactory PublishedModelFactory { get; }
public static bool IsNested(IPublishedPropertyType publishedProperty)
@@ -39,26 +35,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
return IsNested(publishedProperty) && !IsNestedSingle(publishedProperty);
}
protected IPublishedElement ConvertToElement(JObject sourceObject, PropertyCacheLevel referenceCacheLevel, bool preview)
{
var elementTypeAlias = sourceObject[NestedContentPropertyEditor.ContentTypeAliasPropertyKey]?.ToObject<string>();
if (string.IsNullOrEmpty(elementTypeAlias))
return null;
// only convert element types - content types will cause an exception when PublishedModelFactory creates the model
var publishedContentType = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetContentType(elementTypeAlias);
if (publishedContentType == null || publishedContentType.IsElement == false)
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;
IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor);
element = PublishedModelFactory.CreateModel(element);
return element;
}
}
}
+5
View File
@@ -233,8 +233,13 @@
<Compile Include="Mvc\SurfaceControllerTypeCollectionBuilder.cs" />
<Compile Include="Mvc\ValidateUmbracoFormRouteStringAttribute.cs" />
<Compile Include="Profiling\WebProfilingController.cs" />
<Compile Include="PropertyEditors\BlockEditorPropertyEditor.cs" />
<Compile Include="PropertyEditors\BlockListConfiguration.cs" />
<Compile Include="PropertyEditors\BlockListPropertyEditor.cs" />
<Compile Include="PropertyEditors\ParameterEditors\MultipleMediaPickerParameterEditor.cs" />
<Compile Include="PropertyEditors\RichTextEditorPastedImages.cs" />
<Compile Include="PropertyEditors\ValueConverters\BlockEditorConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\BlockListPropertyValueConverter.cs" />
<Compile Include="PublishedCache\NuCache\PublishedSnapshotServiceOptions.cs" />
<Compile Include="PublishedCache\NuCache\Snap\GenObj.cs" />
<Compile Include="PublishedCache\NuCache\Snap\GenRef.cs" />