Merge pull request #6936 from umbraco/AB3325-AB3326-AB3327-AB3328-AB3329-AB3330-AB3331-Media-Tracking-References
media tracking references
This commit is contained in:
@@ -11,6 +11,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.PublishedContent;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web;
|
||||
@@ -33,7 +34,7 @@ namespace Umbraco.Tests.Published
|
||||
var proflog = new ProfilingLogger(logger, profiler);
|
||||
|
||||
PropertyEditorCollection editors = null;
|
||||
var editor = new NestedContentPropertyEditor(logger, new Lazy<PropertyEditorCollection>(() => editors));
|
||||
var editor = new NestedContentPropertyEditor(logger, new Lazy<PropertyEditorCollection>(() => editors), Mock.Of<IDataTypeService>());
|
||||
editors = new PropertyEditorCollection(new DataEditorCollection(new DataEditor[] { editor }));
|
||||
|
||||
var dataType1 = new DataType(editor)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
@@ -25,5 +26,24 @@ namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
return new ContentPickerConfigurationEditor();
|
||||
}
|
||||
|
||||
protected override IDataValueEditor CreateValueEditor() => new ContentPickerPropertyValueEditor(Attribute);
|
||||
|
||||
internal class ContentPickerPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
public ContentPickerPropertyValueEditor(DataEditorAttribute attribute) : base(attribute)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
var asString = value is string str ? str : value?.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(asString)) yield break;
|
||||
|
||||
if (Udi.TryParse(asString, out var udi))
|
||||
yield return new UmbracoEntityReference(udi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,19 @@ namespace Umbraco.Web.PropertyEditors
|
||||
private IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly HtmlImageSourceParser _imageSourceParser;
|
||||
private readonly RichTextEditorPastedImages _pastedImages;
|
||||
private readonly HtmlLocalLinkParser _localLinkParser;
|
||||
|
||||
public GridPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages)
|
||||
public GridPropertyEditor(ILogger logger,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
HtmlImageSourceParser imageSourceParser,
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
HtmlLocalLinkParser localLinkParser)
|
||||
: base(logger)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_imageSourceParser = imageSourceParser;
|
||||
_pastedImages = pastedImages;
|
||||
_localLinkParser = localLinkParser;
|
||||
}
|
||||
|
||||
public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory();
|
||||
@@ -44,22 +50,30 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// Overridden to ensure that the value is validated
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _umbracoContextAccessor, _imageSourceParser, _pastedImages);
|
||||
protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _umbracoContextAccessor, _imageSourceParser, _pastedImages, _localLinkParser);
|
||||
|
||||
protected override IConfigurationEditor CreateConfigurationEditor() => new GridConfigurationEditor();
|
||||
|
||||
internal class GridPropertyValueEditor : DataValueEditor
|
||||
internal class GridPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
private IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly HtmlImageSourceParser _imageSourceParser;
|
||||
private readonly RichTextEditorPastedImages _pastedImages;
|
||||
private readonly RichTextPropertyEditor.RichTextPropertyValueEditor _richTextPropertyValueEditor;
|
||||
private readonly MediaPickerPropertyEditor.MediaPickerPropertyValueEditor _mediaPickerPropertyValueEditor;
|
||||
|
||||
public GridPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages)
|
||||
public GridPropertyValueEditor(DataEditorAttribute attribute,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
HtmlImageSourceParser imageSourceParser,
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
HtmlLocalLinkParser localLinkParser)
|
||||
: base(attribute)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_imageSourceParser = imageSourceParser;
|
||||
_pastedImages = pastedImages;
|
||||
_richTextPropertyValueEditor = new RichTextPropertyEditor.RichTextPropertyValueEditor(attribute, umbracoContextAccessor, imageSourceParser, localLinkParser, pastedImages);
|
||||
_mediaPickerPropertyValueEditor = new MediaPickerPropertyEditor.MediaPickerPropertyValueEditor(attribute);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,7 +98,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
var mediaParent = config?.MediaParentId;
|
||||
var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid;
|
||||
|
||||
var grid = DeserializeGridValue(rawJson, out var rtes);
|
||||
var grid = DeserializeGridValue(rawJson, out var rtes, out _);
|
||||
|
||||
var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId;
|
||||
|
||||
@@ -117,7 +131,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
var val = property.GetValue(culture, segment);
|
||||
if (val == null) return string.Empty;
|
||||
|
||||
var grid = DeserializeGridValue(val.ToString(), out var rtes);
|
||||
var grid = DeserializeGridValue(val.ToString(), out var rtes, out _);
|
||||
|
||||
//process the rte values
|
||||
foreach (var rte in rtes.ToList())
|
||||
@@ -131,16 +145,41 @@ namespace Umbraco.Web.PropertyEditors
|
||||
return grid;
|
||||
}
|
||||
|
||||
private GridValue DeserializeGridValue(string rawJson, out IEnumerable<GridValue.GridControl> richTextValues)
|
||||
private GridValue DeserializeGridValue(string rawJson, out IEnumerable<GridValue.GridControl> richTextValues, out IEnumerable<GridValue.GridControl> mediaValues)
|
||||
{
|
||||
var grid = JsonConvert.DeserializeObject<GridValue>(rawJson);
|
||||
|
||||
// Find all controls that use the RTE editor
|
||||
var controls = grid.Sections.SelectMany(x => x.Rows.SelectMany(r => r.Areas).SelectMany(a => a.Controls));
|
||||
var controls = grid.Sections.SelectMany(x => x.Rows.SelectMany(r => r.Areas).SelectMany(a => a.Controls)).ToArray();
|
||||
richTextValues = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "rte");
|
||||
mediaValues = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "media");
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve references from <see cref="IDataValueEditor"/> values
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString();
|
||||
|
||||
DeserializeGridValue(rawJson, out var richTextEditorValues, out var mediaValues);
|
||||
|
||||
foreach (var umbracoEntityReference in richTextEditorValues.SelectMany(x =>
|
||||
_richTextPropertyValueEditor.GetReferences(x.Value)))
|
||||
{
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
|
||||
foreach (var umbracoEntityReference in mediaValues.SelectMany(x =>
|
||||
_mediaPickerPropertyValueEditor.GetReferences(x.Value["udi"])))
|
||||
{
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Umbraco.Core;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
@@ -17,14 +19,37 @@ namespace Umbraco.Web.PropertyEditors
|
||||
Icon = Constants.Icons.MediaImage)]
|
||||
public class MediaPickerPropertyEditor : DataEditor
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MediaPickerPropertyEditor"/> class.
|
||||
/// </summary>
|
||||
public MediaPickerPropertyEditor(ILogger logger)
|
||||
: base(logger)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPickerConfigurationEditor();
|
||||
|
||||
protected override IDataValueEditor CreateValueEditor() => new MediaPickerPropertyValueEditor(Attribute);
|
||||
|
||||
internal class MediaPickerPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
public MediaPickerPropertyValueEditor(DataEditorAttribute attribute) : base(attribute)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
var asString = value is string str ? str : value?.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(asString)) yield break;
|
||||
|
||||
if (Udi.TryParse(asString, out var udi))
|
||||
yield return new UmbracoEntityReference(udi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Umbraco.Core;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
@@ -18,5 +20,30 @@ namespace Umbraco.Web.PropertyEditors
|
||||
{ }
|
||||
|
||||
protected override IConfigurationEditor CreateConfigurationEditor() => new MultiNodePickerConfigurationEditor();
|
||||
|
||||
protected override IDataValueEditor CreateValueEditor() => new MultiNodeTreePickerPropertyValueEditor(Attribute);
|
||||
|
||||
public class MultiNodeTreePickerPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
public MultiNodeTreePickerPropertyValueEditor(DataEditorAttribute attribute): base(attribute)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
var asString = value == null ? string.Empty : value is string str ? str : value.ToString();
|
||||
|
||||
var udiPaths = asString.Split(',');
|
||||
foreach (var udiPath in udiPaths)
|
||||
{
|
||||
if (Udi.TryParse(udiPath, out var udi))
|
||||
yield return new UmbracoEntityReference(udi);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ using Umbraco.Web.PublishedCache;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
public class MultiUrlPickerValueEditor : DataValueEditor
|
||||
public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly ILogger _logger;
|
||||
@@ -174,5 +174,25 @@ namespace Umbraco.Web.PropertyEditors
|
||||
[DataMember(Name = "queryString")]
|
||||
public string QueryString { get; set; }
|
||||
}
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
var asString = value == null ? string.Empty : value is string str ? str : value.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(asString)) yield break;
|
||||
|
||||
var links = JsonConvert.DeserializeObject<List<LinkDto>>(asString);
|
||||
foreach (var link in links)
|
||||
{
|
||||
if (link.Udi != null) // Links can be absolute links without a Udi
|
||||
{
|
||||
yield return new UmbracoEntityReference(link.Udi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public class NestedContentPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly Lazy<PropertyEditorCollection> _propertyEditors;
|
||||
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
internal const string ContentTypeAliasPropertyKey = "ncContentTypeAlias";
|
||||
|
||||
public NestedContentPropertyEditor(ILogger logger, Lazy<PropertyEditorCollection> propertyEditors)
|
||||
public NestedContentPropertyEditor(ILogger logger, Lazy<PropertyEditorCollection> propertyEditors, IDataTypeService dataTypeService)
|
||||
: base (logger)
|
||||
{
|
||||
_propertyEditors = propertyEditors;
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
// has to be lazy else circular dep in ctor
|
||||
@@ -56,21 +57,21 @@ namespace Umbraco.Web.PropertyEditors
|
||||
|
||||
#region Value Editor
|
||||
|
||||
protected override IDataValueEditor CreateValueEditor() => new NestedContentPropertyValueEditor(Attribute, PropertyEditors);
|
||||
protected override IDataValueEditor CreateValueEditor() => new NestedContentPropertyValueEditor(Attribute, PropertyEditors, _dataTypeService);
|
||||
|
||||
internal class NestedContentPropertyValueEditor : DataValueEditor
|
||||
internal class NestedContentPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public NestedContentPropertyValueEditor(DataEditorAttribute attribute, PropertyEditorCollection propertyEditors)
|
||||
public NestedContentPropertyValueEditor(DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService)
|
||||
: base(attribute)
|
||||
{
|
||||
_propertyEditors = propertyEditors;
|
||||
Validators.Add(new NestedContentValidator(propertyEditors));
|
||||
_dataTypeService = dataTypeService;
|
||||
Validators.Add(new NestedContentValidator(propertyEditors, dataTypeService));
|
||||
}
|
||||
|
||||
internal ServiceContext Services => Current.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Configuration
|
||||
{
|
||||
@@ -87,60 +88,92 @@ namespace Umbraco.Web.PropertyEditors
|
||||
}
|
||||
}
|
||||
|
||||
#region DB to String
|
||||
|
||||
public override string ConvertDbToString(PropertyType propertyType, object propertyValue, IDataTypeService dataTypeService)
|
||||
/// <summary>
|
||||
/// Method used to iterate over the deserialized property values
|
||||
/// </summary>
|
||||
/// <param name="propertyValue"></param>
|
||||
/// <param name="onIteration"></param>
|
||||
internal static List<JObject> IteratePropertyValues(object propertyValue, Action<string, PropertyType, JObject, int> onIteration)
|
||||
{
|
||||
if (propertyValue == null || string.IsNullOrWhiteSpace(propertyValue.ToString()))
|
||||
return string.Empty;
|
||||
return null;
|
||||
|
||||
var value = JsonConvert.DeserializeObject<List<object>>(propertyValue.ToString());
|
||||
if (value == null)
|
||||
return string.Empty;
|
||||
var value = JsonConvert.DeserializeObject<List<JObject>>(propertyValue.ToString());
|
||||
|
||||
// There was a note here about checking if the result had zero items and if so it would return null, so we'll continue to do that
|
||||
// The original note was: "Issue #38 - Keep recursive property lookups working"
|
||||
// Which is from the original NC tracker: https://github.com/umco/umbraco-nested-content/issues/38
|
||||
// This check should be used everywhere when iterating NC prop values, instead of just the one previous place so that
|
||||
// empty values don't get persisted when there is nothing, it should actually be null.
|
||||
if (value == null || value.Count == 0)
|
||||
return null;
|
||||
|
||||
var index = 0;
|
||||
|
||||
foreach (var o in value)
|
||||
{
|
||||
var propValues = (JObject) o;
|
||||
var propValues = o;
|
||||
|
||||
// TODO: This is N+1 (although we cache all doc types, it's still not pretty)
|
||||
var contentType = GetElementType(propValues);
|
||||
if (contentType == null)
|
||||
continue;
|
||||
|
||||
var propAliases = propValues.Properties().Select(x => x.Name).ToArray();
|
||||
var propertyTypes = contentType.CompositionPropertyTypes.ToDictionary(x => x.Alias, x => x);
|
||||
var propAliases = propValues.Properties().Select(x => x.Name);
|
||||
foreach (var propAlias in propAliases)
|
||||
{
|
||||
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propAlias);
|
||||
if (propType == null)
|
||||
propertyTypes.TryGetValue(propAlias, out var propType);
|
||||
onIteration(propAlias, propType, propValues, index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#region DB to String
|
||||
|
||||
public override string ConvertDbToString(PropertyType propertyType, object propertyValue, IDataTypeService dataTypeService)
|
||||
{
|
||||
var value = IteratePropertyValues(propertyValue, (string propAlias, PropertyType propType, JObject propValues, int index) =>
|
||||
{
|
||||
if (propType == null)
|
||||
{
|
||||
// type not found, and property is not system: just delete the value
|
||||
if (IsSystemPropertyKey(propAlias) == false)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// type not found, and property is not system: just delete the value
|
||||
if (IsSystemPropertyKey(propAlias) == false)
|
||||
propValues[propAlias] = null;
|
||||
// convert the value, and store the converted value
|
||||
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
var tempConfig = dataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
var valEditor = propEditor.GetValueEditor(tempConfig);
|
||||
var convValue = valEditor.ConvertDbToString(propType, propValues[propAlias]?.ToString(), dataTypeService);
|
||||
propValues[propAlias] = convValue;
|
||||
}
|
||||
else
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// convert the value, and store the converted value
|
||||
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
var tempConfig = dataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
var valEditor = propEditor.GetValueEditor(tempConfig);
|
||||
var convValue = valEditor.ConvertDbToString(propType, propValues[propAlias]?.ToString(), dataTypeService);
|
||||
propValues[propAlias] = convValue;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// deal with weird situations by ignoring them (no comment)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
// deal with weird situations by ignoring them (no comment)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (value == null)
|
||||
return string.Empty;
|
||||
|
||||
return JsonConvert.SerializeObject(value).ToXmlString<string>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Convert database // editor
|
||||
|
||||
// note: there is NO variant support here
|
||||
@@ -148,58 +181,43 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
|
||||
{
|
||||
var val = property.GetValue(culture, segment);
|
||||
if (val == null || string.IsNullOrWhiteSpace(val.ToString()))
|
||||
return string.Empty;
|
||||
|
||||
var value = JsonConvert.DeserializeObject<List<object>>(val.ToString());
|
||||
var value = IteratePropertyValues(val, (string propAlias, PropertyType propType, JObject propValues, int index) =>
|
||||
{
|
||||
if (propType == null)
|
||||
{
|
||||
// type not found, and property is not system: just delete the value
|
||||
if (IsSystemPropertyKey(propAlias) == false)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// create a temp property with the value
|
||||
// - force it to be culture invariant as NC can't handle culture variant element properties
|
||||
propType.Variations = ContentVariation.Nothing;
|
||||
var tempProp = new Property(propType);
|
||||
tempProp.SetValue(propValues[propAlias] == null ? null : propValues[propAlias].ToString());
|
||||
|
||||
// convert that temp property, and store the converted value
|
||||
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
var tempConfig = dataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
var valEditor = propEditor.GetValueEditor(tempConfig);
|
||||
var convValue = valEditor.ToEditor(tempProp, dataTypeService);
|
||||
propValues[propAlias] = convValue == null ? null : JToken.FromObject(convValue);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// deal with weird situations by ignoring them (no comment)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (value == null)
|
||||
return string.Empty;
|
||||
|
||||
foreach (var o in value)
|
||||
{
|
||||
var propValues = (JObject) o;
|
||||
|
||||
var contentType = GetElementType(propValues);
|
||||
if (contentType == null)
|
||||
continue;
|
||||
|
||||
var propAliases = propValues.Properties().Select(x => x.Name).ToArray();
|
||||
foreach (var propAlias in propAliases)
|
||||
{
|
||||
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propAlias);
|
||||
if (propType == null)
|
||||
{
|
||||
// type not found, and property is not system: just delete the value
|
||||
if (IsSystemPropertyKey(propAlias) == false)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// create a temp property with the value
|
||||
// - force it to be culture invariant as NC can't handle culture variant element properties
|
||||
propType.Variations = ContentVariation.Nothing;
|
||||
var tempProp = new Property(propType);
|
||||
tempProp.SetValue(propValues[propAlias] == null ? null : propValues[propAlias].ToString());
|
||||
|
||||
// convert that temp property, and store the converted value
|
||||
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
var tempConfig = dataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
var valEditor = propEditor.GetValueEditor(tempConfig);
|
||||
var convValue = valEditor.ToEditor(tempProp, dataTypeService);
|
||||
propValues[propAlias] = convValue == null ? null : JToken.FromObject(convValue);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// deal with weird situations by ignoring them (no comment)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// return json
|
||||
return value;
|
||||
}
|
||||
@@ -209,134 +227,121 @@ namespace Umbraco.Web.PropertyEditors
|
||||
if (editorValue.Value == null || string.IsNullOrWhiteSpace(editorValue.Value.ToString()))
|
||||
return null;
|
||||
|
||||
var value = JsonConvert.DeserializeObject<List<object>>(editorValue.Value.ToString());
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// Issue #38 - Keep recursive property lookups working
|
||||
if (!value.Any())
|
||||
return null;
|
||||
|
||||
// Process value
|
||||
for (var i = 0; i < value.Count; i++)
|
||||
var value = IteratePropertyValues(editorValue.Value, (string propAlias, PropertyType propType, JObject propValues, int index) =>
|
||||
{
|
||||
var o = value[i];
|
||||
var propValues = ((JObject)o);
|
||||
|
||||
var contentType = GetElementType(propValues);
|
||||
if (contentType == null)
|
||||
if (propType == null)
|
||||
{
|
||||
continue;
|
||||
// type not found, and property is not system: just delete the value
|
||||
if (IsSystemPropertyKey(propAlias) == false)
|
||||
propValues[propAlias] = null;
|
||||
}
|
||||
|
||||
var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();
|
||||
|
||||
foreach (var propKey in propValueKeys)
|
||||
else
|
||||
{
|
||||
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propKey);
|
||||
if (propType == null)
|
||||
{
|
||||
if (IsSystemPropertyKey(propKey) == false)
|
||||
{
|
||||
// Property missing so just delete the value
|
||||
propValues[propKey] = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fetch the property types prevalue
|
||||
var propConfiguration = Services.DataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
// Fetch the property types prevalue
|
||||
var propConfiguration = _dataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
|
||||
// Lookup the property editor
|
||||
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
// Lookup the property editor
|
||||
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
|
||||
// Create a fake content property data object
|
||||
var contentPropData = new ContentPropertyData(propValues[propKey], propConfiguration);
|
||||
// Create a fake content property data object
|
||||
var contentPropData = new ContentPropertyData(propValues[propAlias], propConfiguration);
|
||||
|
||||
// Get the property editor to do it's conversion
|
||||
var newValue = propEditor.GetValueEditor().FromEditor(contentPropData, propValues[propKey]);
|
||||
|
||||
// Store the value back
|
||||
propValues[propKey] = (newValue == null) ? null : JToken.FromObject(newValue);
|
||||
}
|
||||
// Get the property editor to do it's conversion
|
||||
var newValue = propEditor.GetValueEditor().FromEditor(contentPropData, propValues[propAlias]);
|
||||
|
||||
// Store the value back
|
||||
propValues[propAlias] = (newValue == null) ? null : JToken.FromObject(newValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (value == null)
|
||||
return string.Empty;
|
||||
|
||||
// return json
|
||||
return JsonConvert.SerializeObject(value);
|
||||
}
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString();
|
||||
|
||||
var result = new List<UmbracoEntityReference>();
|
||||
|
||||
var json = IteratePropertyValues(rawJson, (string propAlias, PropertyType propType, JObject propValues, int index) =>
|
||||
{
|
||||
if (propType == null) return;
|
||||
|
||||
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
|
||||
var valueEditor = propEditor?.GetValueEditor();
|
||||
if (!(valueEditor is IDataValueReference reference)) return;
|
||||
|
||||
var val = propValues[propAlias]?.ToString();
|
||||
|
||||
var refs = reference.GetReferences(val);
|
||||
|
||||
result.AddRange(refs);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal class NestedContentValidator : IValueValidator
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public NestedContentValidator(PropertyEditorCollection propertyEditors)
|
||||
public NestedContentValidator(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService)
|
||||
{
|
||||
_propertyEditors = propertyEditors;
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(object rawValue, string valueType, object dataTypeConfiguration)
|
||||
{
|
||||
if (rawValue == null)
|
||||
yield break;
|
||||
var validationResults = new List<ValidationResult>();
|
||||
|
||||
var value = JsonConvert.DeserializeObject<List<object>>(rawValue.ToString());
|
||||
if (value == null)
|
||||
yield break;
|
||||
|
||||
var dataTypeService = Current.Services.DataTypeService;
|
||||
for (var i = 0; i < value.Count; i++)
|
||||
NestedContentPropertyValueEditor.IteratePropertyValues(rawValue, (string propKey, PropertyType propType, JObject propValues, int i) =>
|
||||
{
|
||||
var o = value[i];
|
||||
var propValues = (JObject) o;
|
||||
if (propType == null) return;
|
||||
|
||||
var contentType = GetElementType(propValues);
|
||||
if (contentType == null) continue;
|
||||
var config = _dataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
var propertyEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
|
||||
var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray();
|
||||
|
||||
foreach (var propKey in propValueKeys)
|
||||
foreach (var validator in propertyEditor.GetValueEditor().Validators)
|
||||
{
|
||||
var propType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propKey);
|
||||
if (propType != null)
|
||||
foreach (var result in validator.Validate(propValues[propKey], propertyEditor.GetValueEditor().ValueType, config))
|
||||
{
|
||||
var config = dataTypeService.GetDataType(propType.DataTypeId).Configuration;
|
||||
var propertyEditor = _propertyEditors[propType.PropertyEditorAlias];
|
||||
|
||||
foreach (var validator in propertyEditor.GetValueEditor().Validators)
|
||||
{
|
||||
foreach (var result in validator.Validate(propValues[propKey], propertyEditor.GetValueEditor().ValueType, config))
|
||||
{
|
||||
result.ErrorMessage = "Item " + (i + 1) + " '" + propType.Name + "' " + result.ErrorMessage;
|
||||
yield return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Check mandatory
|
||||
if (propType.Mandatory)
|
||||
{
|
||||
if (propValues[propKey] == null)
|
||||
yield return new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be null", new[] { propKey });
|
||||
else if (propValues[propKey].ToString().IsNullOrWhiteSpace() || (propValues[propKey].Type == JTokenType.Array && !propValues[propKey].HasValues))
|
||||
yield return new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be empty", new[] { propKey });
|
||||
}
|
||||
|
||||
// Check regex
|
||||
if (!propType.ValidationRegExp.IsNullOrWhiteSpace()
|
||||
&& propValues[propKey] != null && !propValues[propKey].ToString().IsNullOrWhiteSpace())
|
||||
{
|
||||
var regex = new Regex(propType.ValidationRegExp);
|
||||
if (!regex.IsMatch(propValues[propKey].ToString()))
|
||||
{
|
||||
yield return new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' is invalid, it does not match the correct pattern", new[] { propKey });
|
||||
}
|
||||
}
|
||||
result.ErrorMessage = "Item " + (i + 1) + " '" + propType.Name + "' " + result.ErrorMessage;
|
||||
validationResults.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check mandatory
|
||||
if (propType.Mandatory)
|
||||
{
|
||||
if (propValues[propKey] == null)
|
||||
validationResults.Add(new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be null", new[] { propKey }));
|
||||
else if (propValues[propKey].ToString().IsNullOrWhiteSpace() || (propValues[propKey].Type == JTokenType.Array && !propValues[propKey].HasValues))
|
||||
validationResults.Add(new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be empty", new[] { propKey }));
|
||||
}
|
||||
|
||||
// Check regex
|
||||
if (!propType.ValidationRegExp.IsNullOrWhiteSpace()
|
||||
&& propValues[propKey] != null && !propValues[propKey].ToString().IsNullOrWhiteSpace())
|
||||
{
|
||||
var regex = new Regex(propType.ValidationRegExp);
|
||||
if (!regex.IsMatch(propValues[propKey].ToString()))
|
||||
{
|
||||
validationResults.Add(new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' is invalid, it does not match the correct pattern", new[] { propKey }));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return validationResults;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user