post merge
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.Models.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// The base class for any strongly typed model for a Block editor implementation
|
||||
/// </summary>
|
||||
public abstract class BlockEditorModel
|
||||
{
|
||||
protected BlockEditorModel(IEnumerable<IPublishedElement> contentData, IEnumerable<IPublishedElement> settingsData)
|
||||
{
|
||||
ContentData = contentData ?? throw new ArgumentNullException(nameof(contentData));
|
||||
SettingsData = settingsData ?? new List<IPublishedContent>();
|
||||
}
|
||||
|
||||
public BlockEditorModel()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The content data items of the Block List editor
|
||||
/// </summary>
|
||||
[DataMember(Name = "contentData")]
|
||||
public IEnumerable<IPublishedElement> ContentData { get; set; } = new List<IPublishedContent>();
|
||||
|
||||
/// <summary>
|
||||
/// The settings data items of the Block List editor
|
||||
/// </summary>
|
||||
[DataMember(Name = "settingsData")]
|
||||
public IEnumerable<IPublishedElement> SettingsData { get; set; } = new List<IPublishedContent>();
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,14 @@ namespace Umbraco.Core.Models.Blocks
|
||||
/// </summary>
|
||||
public class BlockPropertyValue
|
||||
{
|
||||
public object Value { get; set; }
|
||||
public IPropertyType PropertyType { get; set; }
|
||||
public BlockPropertyValue(object value, IPropertyType propertyType)
|
||||
{
|
||||
Value = value;
|
||||
PropertyType = propertyType ?? throw new ArgumentNullException(nameof(propertyType));
|
||||
}
|
||||
|
||||
public object Value { get; }
|
||||
public IPropertyType PropertyType { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.Models.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a layout item for the Block List editor
|
||||
/// </summary>
|
||||
[DataContract(Name = "blockListLayout", Namespace = "")]
|
||||
public class BlockListLayoutReference : IBlockReference<IPublishedElement>
|
||||
{
|
||||
public BlockListLayoutReference(Udi contentUdi, IPublishedElement content, Udi settingsUdi, IPublishedElement settings)
|
||||
{
|
||||
ContentUdi = contentUdi ?? throw new ArgumentNullException(nameof(contentUdi));
|
||||
Content = content ?? throw new ArgumentNullException(nameof(content));
|
||||
Settings = settings; // can be null
|
||||
SettingsUdi = settingsUdi; // can be null
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Id of the content data item
|
||||
/// </summary>
|
||||
[DataMember(Name = "contentUdi")]
|
||||
public Udi ContentUdi { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Id of the settings data item
|
||||
/// </summary>
|
||||
[DataMember(Name = "settingsUdi")]
|
||||
public Udi SettingsUdi { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The content data item referenced
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is ignored from serialization since it is just a reference to the actual data element
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public IPublishedElement Content { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The settings data item referenced
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is ignored from serialization since it is just a reference to the actual data element
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public IPublishedElement Settings { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
@@ -8,26 +11,54 @@ namespace Umbraco.Core.Models.Blocks
|
||||
/// The strongly typed model for the Block List editor
|
||||
/// </summary>
|
||||
[DataContract(Name = "blockList", Namespace = "")]
|
||||
public class BlockListModel : BlockEditorModel
|
||||
public class BlockListModel : IReadOnlyList<BlockListItem>
|
||||
{
|
||||
private readonly IReadOnlyList<BlockListItem> _layout = new List<BlockListItem>();
|
||||
|
||||
public static BlockListModel Empty { get; } = new BlockListModel();
|
||||
|
||||
private BlockListModel()
|
||||
{
|
||||
}
|
||||
|
||||
public BlockListModel(IEnumerable<IPublishedElement> contentData, IEnumerable<IPublishedElement> settingsData, IEnumerable<BlockListLayoutReference> layout)
|
||||
: base(contentData, settingsData)
|
||||
public BlockListModel(IEnumerable<BlockListItem> layout)
|
||||
{
|
||||
Layout = layout;
|
||||
_layout = layout.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The layout items of the Block List editor
|
||||
/// </summary>
|
||||
[DataMember(Name = "layout")]
|
||||
public IEnumerable<BlockListLayoutReference> Layout { get; } = new List<BlockListLayoutReference>();
|
||||
public int Count => _layout.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Get the block by index
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public BlockListItem this[int index] => _layout[index];
|
||||
|
||||
/// <summary>
|
||||
/// Get the block by content Guid
|
||||
/// </summary>
|
||||
/// <param name="contentKey"></param>
|
||||
/// <returns></returns>
|
||||
public BlockListItem this[Guid contentKey] => _layout.FirstOrDefault(x => x.Content.Key == contentKey);
|
||||
|
||||
/// <summary>
|
||||
/// Get the block by content element Udi
|
||||
/// </summary>
|
||||
/// <param name="contentUdi"></param>
|
||||
/// <returns></returns>
|
||||
public BlockListItem this[Udi contentUdi]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!(contentUdi is GuidUdi guidUdi)) return null;
|
||||
return _layout.FirstOrDefault(x => x.Content.Key == guidUdi.Guid);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<BlockListItem> GetEnumerator() => _layout.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Blocks
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data item reference for a Block editor implementation
|
||||
/// </summary>
|
||||
/// <typeparam name="TSettings"></typeparam>
|
||||
/// <remarks>
|
||||
/// see: https://github.com/umbraco/rfcs/blob/907f3758cf59a7b6781296a60d57d537b3b60b8c/cms/0011-block-data-structure.md#strongly-typed
|
||||
/// </remarks>
|
||||
public interface IBlockReference<TSettings> : IBlockReference
|
||||
{
|
||||
TSettings Settings { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data item reference for a Block Editor implementation
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// see: https://github.com/umbraco/rfcs/blob/907f3758cf59a7b6781296a60d57d537b3b60b8c/cms/0011-block-data-structure.md#strongly-typed
|
||||
/// </remarks>
|
||||
public interface IBlockReference
|
||||
{
|
||||
Udi ContentUdi { get; }
|
||||
}
|
||||
}
|
||||
@@ -47,17 +47,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
public ContentTypeBasic GetContentType(IContentBase source, MapperContext context)
|
||||
{
|
||||
|
||||
var user = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser;
|
||||
if (user?.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)) ?? false)
|
||||
{
|
||||
var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source);
|
||||
var contentTypeBasic = context.Map<IContentTypeComposition, ContentTypeBasic>(contentType);
|
||||
|
||||
return contentTypeBasic;
|
||||
}
|
||||
//no access
|
||||
return null;
|
||||
var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source);
|
||||
var contentTypeBasic = context.Map<IContentTypeComposition, ContentTypeBasic>(contentType);
|
||||
return contentTypeBasic;
|
||||
}
|
||||
|
||||
public IEnumerable<ContentApp> GetContentApps(IUmbracoEntity source)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
internal class BlockEditorPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IDataTypeService _dataTypeService; // TODO: Not used yet but we'll need it to fill in the FromEditor/ToEditor
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly BlockEditorValues _blockEditorValues;
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
_dataTypeService = dataTypeService;
|
||||
_logger = logger;
|
||||
_blockEditorValues = new BlockEditorValues(new BlockListEditorDataConverter(), contentTypeService, _logger);
|
||||
Validators.Add(new BlockEditorValidator(_blockEditorValues, propertyEditors, dataTypeService, textService));
|
||||
Validators.Add(new BlockEditorValidator(_blockEditorValues, propertyEditors, dataTypeService, textService, contentTypeService));
|
||||
Validators.Add(new MinMaxValidator(_blockEditorValues, textService));
|
||||
}
|
||||
|
||||
@@ -246,19 +246,33 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public IEnumerable<ValidationResult> Validate(object value, string valueType, object dataTypeConfiguration)
|
||||
{
|
||||
var blockConfig = (BlockListConfiguration)dataTypeConfiguration;
|
||||
if (blockConfig == null) yield break;
|
||||
|
||||
var validationLimit = blockConfig.ValidationLimit;
|
||||
if (validationLimit == null) yield break;
|
||||
|
||||
var blockEditorData = _blockEditorValues.DeserializeAndClean(value);
|
||||
if ((blockEditorData == null && blockConfig?.ValidationLimit?.Min > 0)
|
||||
|| (blockEditorData != null && blockEditorData.Layout.Count() < blockConfig?.ValidationLimit?.Min))
|
||||
|
||||
if ((blockEditorData == null && validationLimit.Min.HasValue && validationLimit.Min > 0)
|
||||
|| (blockEditorData != null && validationLimit.Min.HasValue && blockEditorData.Layout.Count() < validationLimit.Min))
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
_textService.Localize("validation/entriesShort", new[] { blockConfig.ValidationLimit.Min.ToString(), (blockConfig.ValidationLimit.Min - blockEditorData.Layout.Count()).ToString() }),
|
||||
_textService.Localize("validation/entriesShort", new[]
|
||||
{
|
||||
validationLimit.Min.ToString(),
|
||||
(validationLimit.Min - blockEditorData.Layout.Count()).ToString()
|
||||
}),
|
||||
new[] { "minCount" });
|
||||
}
|
||||
|
||||
if (blockEditorData != null && blockEditorData.Layout.Count() > blockConfig?.ValidationLimit?.Max)
|
||||
if (blockEditorData != null && validationLimit.Max.HasValue && blockEditorData.Layout.Count() > validationLimit.Max)
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
_textService.Localize("validation/entriesExceed", new[] { blockConfig.ValidationLimit.Max.ToString(), (blockEditorData.Layout.Count() - blockConfig.ValidationLimit.Max).ToString() }),
|
||||
_textService.Localize("validation/entriesExceed", new[]
|
||||
{
|
||||
validationLimit.Max.ToString(),
|
||||
(blockEditorData.Layout.Count() - validationLimit.Max).ToString()
|
||||
}),
|
||||
new[] { "maxCount" });
|
||||
}
|
||||
}
|
||||
@@ -267,10 +281,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
internal class BlockEditorValidator : ComplexEditorValidator
|
||||
{
|
||||
private readonly BlockEditorValues _blockEditorValues;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public BlockEditorValidator(BlockEditorValues blockEditorValues, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService) : base(propertyEditors, dataTypeService, textService)
|
||||
public BlockEditorValidator(BlockEditorValues blockEditorValues, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService, IContentTypeService contentTypeService)
|
||||
: base(propertyEditors, dataTypeService, textService)
|
||||
{
|
||||
_blockEditorValues = blockEditorValues;
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ElementTypeValidationModel> GetElementTypeValidation(object value)
|
||||
@@ -278,8 +295,28 @@ namespace Umbraco.Web.PropertyEditors
|
||||
var blockEditorData = _blockEditorValues.DeserializeAndClean(value);
|
||||
if (blockEditorData != null)
|
||||
{
|
||||
foreach (var row in blockEditorData.BlockValue.ContentData.Concat(blockEditorData.BlockValue.SettingsData))
|
||||
// There is no guarantee that the client will post data for every property defined in the Element Type but we still
|
||||
// need to validate that data for each property especially for things like 'required' data to work.
|
||||
// Lookup all element types for all content/settings and then we can populate any empty properties.
|
||||
var allElements = blockEditorData.BlockValue.ContentData.Concat(blockEditorData.BlockValue.SettingsData).ToList();
|
||||
var allElementTypes = _contentTypeService.GetAll(allElements.Select(x => x.ContentTypeKey).ToArray()).ToDictionary(x => x.Key);
|
||||
|
||||
foreach (var row in allElements)
|
||||
{
|
||||
if (!allElementTypes.TryGetValue(row.ContentTypeKey, out var elementType))
|
||||
throw new InvalidOperationException($"No element type found with key {row.ContentTypeKey}");
|
||||
|
||||
// now ensure missing properties
|
||||
foreach (var elementTypeProp in elementType.CompositionPropertyTypes)
|
||||
{
|
||||
if (!row.PropertyValues.ContainsKey(elementTypeProp.Alias))
|
||||
{
|
||||
// set values to null
|
||||
row.PropertyValues[elementTypeProp.Alias] = new BlockPropertyValue(null, elementTypeProp);
|
||||
row.RawPropertyValues[elementTypeProp.Alias] = null;
|
||||
}
|
||||
}
|
||||
|
||||
var elementValidation = new ElementTypeValidationModel(row.ContentTypeAlias, row.Key);
|
||||
foreach (var prop in row.PropertyValues)
|
||||
{
|
||||
@@ -331,7 +368,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
var contentTypePropertyTypes = new Dictionary<string, Dictionary<string, IPropertyType>>();
|
||||
|
||||
// filter out any content that isn't referenced in the layout references
|
||||
foreach(var block in blockEditorData.BlockValue.ContentData.Where(x => blockEditorData.References.Any(r => r.ContentUdi == x.Udi)))
|
||||
foreach (var block in blockEditorData.BlockValue.ContentData.Where(x => blockEditorData.References.Any(r => r.ContentUdi == x.Udi)))
|
||||
{
|
||||
ResolveBlockItemData(block, contentTypePropertyTypes);
|
||||
}
|
||||
@@ -374,11 +411,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
else
|
||||
{
|
||||
// set the value to include the resolved property type
|
||||
propValues[prop.Key] = new BlockPropertyValue
|
||||
{
|
||||
PropertyType = propType,
|
||||
Value = prop.Value
|
||||
};
|
||||
propValues[prop.Key] = new BlockPropertyValue(prop.Value, propType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
[JsonProperty("thumbnail")]
|
||||
public string Thumbnail { get; set; }
|
||||
|
||||
// TODO: This is named inconsistently in JS but renaming it needs to be done in quite a lot of places, this should be contentElementTypeKey
|
||||
[JsonProperty("contentTypeKey")]
|
||||
[JsonProperty("contentElementTypeKey")]
|
||||
public Guid ContentElementTypeKey { get; set; }
|
||||
|
||||
[JsonProperty("settingsElementTypeKey")]
|
||||
|
||||
@@ -90,11 +90,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
_dataTypeService = dataTypeService;
|
||||
_logger = logger;
|
||||
_nestedContentValues = new NestedContentValues(contentTypeService);
|
||||
Validators.Add(new NestedContentValidator(_nestedContentValues, propertyEditors, dataTypeService, localizedTextService));
|
||||
Validators.Add(new NestedContentValidator(_nestedContentValues, propertyEditors, dataTypeService, localizedTextService, contentTypeService));
|
||||
|
||||
_contentTypes = new Lazy<Dictionary<string, IContentType>>(() =>
|
||||
_contentTypeService.GetAll().ToDictionary(c => c.Alias)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -300,16 +301,47 @@ namespace Umbraco.Web.PropertyEditors
|
||||
internal class NestedContentValidator : ComplexEditorValidator
|
||||
{
|
||||
private readonly NestedContentValues _nestedContentValues;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public NestedContentValidator(NestedContentValues nestedContentValues, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService)
|
||||
public NestedContentValidator(NestedContentValues nestedContentValues, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService, IContentTypeService contentTypeService)
|
||||
: base(propertyEditors, dataTypeService, textService)
|
||||
{
|
||||
_nestedContentValues = nestedContentValues;
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
protected override IEnumerable<ElementTypeValidationModel> GetElementTypeValidation(object value)
|
||||
{
|
||||
foreach (var row in _nestedContentValues.GetPropertyValues(value))
|
||||
var rows = _nestedContentValues.GetPropertyValues(value);
|
||||
if (rows.Count == 0) yield break;
|
||||
|
||||
// There is no guarantee that the client will post data for every property defined in the Element Type but we still
|
||||
// need to validate that data for each property especially for things like 'required' data to work.
|
||||
// Lookup all element types for all content/settings and then we can populate any empty properties.
|
||||
var allElementAliases = rows.Select(x => x.ContentTypeAlias).ToList();
|
||||
// unfortunately we need to get all content types and post filter - but they are cached so its ok, there's
|
||||
// no overload to lookup by many aliases.
|
||||
var allElementTypes = _contentTypeService.GetAll().Where(x => allElementAliases.Contains(x.Alias)).ToDictionary(x => x.Alias);
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
if (!allElementTypes.TryGetValue(row.ContentTypeAlias, out var elementType))
|
||||
throw new InvalidOperationException($"No element type found with alias {row.ContentTypeAlias}");
|
||||
|
||||
// now ensure missing properties
|
||||
foreach (var elementTypeProp in elementType.CompositionPropertyTypes)
|
||||
{
|
||||
if (!row.PropertyValues.ContainsKey(elementTypeProp.Alias))
|
||||
{
|
||||
// set values to null
|
||||
row.PropertyValues[elementTypeProp.Alias] = new NestedContentValues.NestedContentPropertyValue
|
||||
{
|
||||
PropertyType = elementTypeProp,
|
||||
Value = null
|
||||
};
|
||||
row.RawPropertyValues[elementTypeProp.Alias] = null;
|
||||
}
|
||||
}
|
||||
|
||||
var elementValidation = new ElementTypeValidationModel(row.ContentTypeAlias, row.Id);
|
||||
foreach (var prop in row.PropertyValues)
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
|
||||
var contentPublishedElements = new Dictionary<Guid, IPublishedElement>();
|
||||
var settingsPublishedElements = new Dictionary<Guid, IPublishedElement>();
|
||||
|
||||
var layout = new List<BlockListLayoutReference>();
|
||||
var layout = new List<BlockListItem>();
|
||||
|
||||
var value = (string)inter;
|
||||
if (string.IsNullOrWhiteSpace(value)) return BlockListModel.Empty;
|
||||
@@ -120,11 +120,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
|
||||
settingsData = null;
|
||||
}
|
||||
|
||||
var layoutRef = new BlockListLayoutReference(contentGuidUdi, contentData, settingGuidUdi, settingsData);
|
||||
var layoutRef = new BlockListItem(contentGuidUdi, contentData, settingGuidUdi, settingsData);
|
||||
layout.Add(layoutRef);
|
||||
}
|
||||
|
||||
var model = new BlockListModel(contentPublishedElements.Values, settingsPublishedElements.Values, layout);
|
||||
var model = new BlockListModel(layout);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user