Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce421b787e | ||
|
|
6be5a8cf35 | ||
|
|
624b5fa640 | ||
|
|
debe205d0e | ||
|
|
2a64bad893 | ||
|
|
9774293f71 | ||
|
|
c607af1f85 | ||
|
|
9fbc325bca | ||
|
|
464661db63 | ||
|
|
170f25118c | ||
|
|
c50f1845f6 |
@@ -23,7 +23,10 @@ public abstract class BlockEditorValidatorBase<TValue, TLayout> : ComplexEditorV
|
||||
var elementTypeValidation = new List<ElementTypeValidationModel>();
|
||||
var isWildcardCulture = validationContext.Culture == "*";
|
||||
var validationContextCulture = isWildcardCulture ? null : validationContext.Culture.NullOrWhiteSpaceAsNull();
|
||||
elementTypeValidation.AddRange(GetBlockEditorDataValidation(blockEditorData, validationContextCulture, validationContext.Segment));
|
||||
foreach (var segment in validationContext.SegmentsBeingValidated.DefaultIfEmpty(null))
|
||||
{
|
||||
elementTypeValidation.AddRange(GetBlockEditorDataValidation(blockEditorData, validationContextCulture, segment));
|
||||
}
|
||||
|
||||
if (validationContextCulture is null)
|
||||
{
|
||||
@@ -119,12 +122,9 @@ public abstract class BlockEditorValidatorBase<TValue, TLayout> : ComplexEditorV
|
||||
continue;
|
||||
}
|
||||
|
||||
if (segment != "*")
|
||||
if (segment != "*" && blockPropertyValue.Segment.InvariantEquals(segment) is false)
|
||||
{
|
||||
if (propertyType.VariesBySegment() != (segment is not null) || blockPropertyValue.Segment.InvariantEquals(segment) is false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
elementValidation.AddPropertyTypeValidation(
|
||||
|
||||
@@ -33,7 +33,7 @@ public sealed class BlockEditorConverter
|
||||
_blockEditorVarianceHandler = blockEditorVarianceHandler;
|
||||
}
|
||||
|
||||
public IPublishedElement? ConvertToElement(IPublishedElement owner, BlockItemData data, PropertyCacheLevel referenceCacheLevel, bool preview)
|
||||
public IPublishedElement? ConvertToElement(IPublishedElement owner, BlockItemData data, IEnumerable<BlockItemVariation> variations, PropertyCacheLevel referenceCacheLevel, bool preview)
|
||||
{
|
||||
// Only convert element types - content types will cause an exception when PublishedModelFactory creates the model
|
||||
IPublishedContentType? publishedContentType = _publishedContentTypeCache.Get(PublishedItemType.Element, data.ContentTypeKey);
|
||||
@@ -48,6 +48,10 @@ public sealed class BlockEditorConverter
|
||||
.PropertyTypes
|
||||
.ToDictionary(propertyType => propertyType.Alias);
|
||||
|
||||
var variationKeys = variations
|
||||
.Select(v => VariationKey(v.Culture, v.Segment))
|
||||
.ToArray();
|
||||
|
||||
var propertyValues = new Dictionary<string, object?>();
|
||||
foreach (BlockPropertyValue property in data.Values)
|
||||
{
|
||||
@@ -72,6 +76,11 @@ public sealed class BlockEditorConverter
|
||||
? variationContext.Segment
|
||||
: null;
|
||||
|
||||
if (expectedSegment is not null && variationKeys.Contains(VariationKey(expectedCulture, expectedSegment)) is false)
|
||||
{
|
||||
expectedSegment = null;
|
||||
}
|
||||
|
||||
if (alignedProperty.Culture.NullOrWhiteSpaceAsNull().InvariantEquals(expectedCulture.NullOrWhiteSpaceAsNull())
|
||||
&& alignedProperty.Segment.NullOrWhiteSpaceAsNull().InvariantEquals(expectedSegment.NullOrWhiteSpaceAsNull()))
|
||||
{
|
||||
@@ -95,6 +104,8 @@ public sealed class BlockEditorConverter
|
||||
element = _publishedModelFactory.CreateModel(element);
|
||||
|
||||
return element;
|
||||
|
||||
string VariationKey(string? culture, string? segment) => $"{culture}:{segment}";
|
||||
}
|
||||
|
||||
public Type GetModelType(Guid contentTypeKey)
|
||||
|
||||
+13
-3
@@ -137,7 +137,8 @@ internal abstract class BlockPropertyValueCreatorBase<TBlockModel, TBlockItemMod
|
||||
continue;
|
||||
}
|
||||
|
||||
IPublishedElement? element = BlockEditorConverter.ConvertToElement(owner, data, referenceCacheLevel, preview);
|
||||
IEnumerable<BlockItemVariation> variations = converted.BlockValue.Expose.Where(e => e.ContentKey == data.Key);
|
||||
IPublishedElement? element = BlockEditorConverter.ConvertToElement(owner, data, variations, referenceCacheLevel, preview);
|
||||
if (element == null)
|
||||
{
|
||||
continue;
|
||||
@@ -154,7 +155,7 @@ internal abstract class BlockPropertyValueCreatorBase<TBlockModel, TBlockItemMod
|
||||
: null;
|
||||
if (expose.Any(v =>
|
||||
v.ContentKey == element.Key && v.Culture == expectedBlockVariationCulture &&
|
||||
v.Segment == expectedBlockVariationSegment) is false)
|
||||
(v.Segment == expectedBlockVariationSegment || v.Segment is null)) is false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -172,6 +173,9 @@ internal abstract class BlockPropertyValueCreatorBase<TBlockModel, TBlockItemMod
|
||||
var settingsPublishedElements = new Dictionary<Guid, IPublishedElement>();
|
||||
var validSettingsElementTypes = blockConfigMap.Values.Select(x => x.SettingsElementTypeKey)
|
||||
.Where(x => x.HasValue).Distinct().ToList();
|
||||
var contentSettingsKeyMapping = converted.References
|
||||
.Where(r => r.SettingsKey.HasValue)
|
||||
.ToDictionary(c => c.SettingsKey!.Value, c => c.ContentKey);
|
||||
foreach (BlockItemData data in converted.BlockValue.SettingsData)
|
||||
{
|
||||
if (!validSettingsElementTypes.Contains(data.ContentTypeKey))
|
||||
@@ -179,7 +183,13 @@ internal abstract class BlockPropertyValueCreatorBase<TBlockModel, TBlockItemMod
|
||||
continue;
|
||||
}
|
||||
|
||||
IPublishedElement? element = BlockEditorConverter.ConvertToElement(owner, data, referenceCacheLevel, preview);
|
||||
if (contentSettingsKeyMapping.TryGetValue(data.Key, out Guid contentKey) is false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IEnumerable<BlockItemVariation> variations = converted.BlockValue.Expose.Where(e => e.ContentKey == contentKey);
|
||||
IPublishedElement? element = BlockEditorConverter.ConvertToElement(owner, data, variations, referenceCacheLevel, preview);
|
||||
if (element is null)
|
||||
{
|
||||
continue;
|
||||
|
||||
-1
@@ -206,7 +206,6 @@ export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement i
|
||||
|
||||
#renderVaryBySegmentProperty() {
|
||||
if (!this._useSegments) return nothing;
|
||||
if (this._isElement) return nothing;
|
||||
|
||||
return html`
|
||||
<umb-property-layout
|
||||
|
||||
+128
@@ -2023,4 +2023,132 @@ internal partial class BlockListElementLevelVariationTests
|
||||
Assert.IsEmpty(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Missing_Segment_Performs_Fallback_To_Default_Segment()
|
||||
{
|
||||
var elementType = CreateElementType(ContentVariation.Segment);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(ContentVariation.Segment, blockListDataType);
|
||||
|
||||
var content = CreateContent(
|
||||
contentType,
|
||||
elementType,
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant content value" },
|
||||
new() { Alias = "variantText", Value = "The variant content value (Default)" },
|
||||
new() { Alias = "variantText", Value = "The variant content value (Segment 2)", Segment = "s2" }
|
||||
},
|
||||
[],
|
||||
true);
|
||||
|
||||
AssertPropertyValues(null, "The invariant content value", "The variant content value (Default)");
|
||||
|
||||
AssertPropertyValues("s1", "The invariant content value", "The variant content value (Default)");
|
||||
|
||||
AssertPropertyValues("s2", "The invariant content value", "The variant content value (Segment 2)");
|
||||
|
||||
var blockListValue = JsonSerializer.Deserialize<BlockListValue>((string)content.Properties["blocks"]!.GetValue()!);
|
||||
blockListValue.ContentData[0].Values.Add(new BlockPropertyValue { Alias = "variantText", Value = "The variant content value (Segment 1)", Segment = "s1" });
|
||||
|
||||
blockListValue.Expose =
|
||||
[
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key },
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key, Segment = "s1" },
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key, Segment = "s2" },
|
||||
];
|
||||
|
||||
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
|
||||
ContentService.Save(content);
|
||||
PublishContent(content, contentType);
|
||||
|
||||
AssertPropertyValues(null, "The invariant content value", "The variant content value (Default)");
|
||||
|
||||
AssertPropertyValues("s1", "The invariant content value", "The variant content value (Segment 1)");
|
||||
|
||||
AssertPropertyValues("s2", "The invariant content value", "The variant content value (Segment 2)");
|
||||
|
||||
void AssertPropertyValues(string? segment, string expectedInvariantContentValue, string expectedVariantContentValue)
|
||||
{
|
||||
SetVariationContext(null, segment);
|
||||
var publishedContent = GetPublishedContent(content.Key);
|
||||
|
||||
var value = publishedContent.Value<BlockListModel>("blocks");
|
||||
Assert.IsNotNull(value);
|
||||
Assert.AreEqual(1, value.Count);
|
||||
|
||||
var blockListItem = value.First();
|
||||
Assert.AreEqual(2, blockListItem.Content.Properties.Count());
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expectedInvariantContentValue, blockListItem.Content.Value<string>("invariantText"));
|
||||
Assert.AreEqual(expectedVariantContentValue, blockListItem.Content.Value<string>("variantText"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Missing_Property_Value_For_Existing_Segment_Does_Not_Perform_Fallback_To_Default_Segment()
|
||||
{
|
||||
var elementType = CreateElementType(ContentVariation.Segment);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(ContentVariation.Segment, blockListDataType);
|
||||
|
||||
var content = CreateContent(contentType, elementType, [], false);
|
||||
var blockListValue = BlockListPropertyValue(
|
||||
elementType,
|
||||
[
|
||||
(
|
||||
Guid.NewGuid(),
|
||||
Guid.NewGuid(),
|
||||
new BlockProperty(
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant content value" },
|
||||
new() { Alias = "variantText", Value = "The variant content value (Default)" },
|
||||
new() { Alias = "variantText", Value = "The variant content value (Segment 2)", Segment = "s2"}
|
||||
},
|
||||
[],
|
||||
null,
|
||||
null)
|
||||
)
|
||||
]);
|
||||
|
||||
blockListValue.Expose =
|
||||
[
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key },
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key, Segment = "s1" },
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key, Segment = "s2" },
|
||||
];
|
||||
|
||||
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
|
||||
ContentService.Save(content);
|
||||
PublishContent(content, contentType);
|
||||
|
||||
AssertPropertyValues(null, "The invariant content value", "The variant content value (Default)");
|
||||
|
||||
AssertPropertyValues("s1", "The invariant content value", string.Empty);
|
||||
|
||||
AssertPropertyValues("s2", "The invariant content value", "The variant content value (Segment 2)");
|
||||
|
||||
|
||||
void AssertPropertyValues(string? segment, string expectedInvariantContentValue, string expectedVariantContentValue)
|
||||
{
|
||||
SetVariationContext(null, segment);
|
||||
var publishedContent = GetPublishedContent(content.Key);
|
||||
|
||||
var value = publishedContent.Value<BlockListModel>("blocks");
|
||||
Assert.IsNotNull(value);
|
||||
Assert.AreEqual(1, value.Count);
|
||||
|
||||
var blockListItem = value.First();
|
||||
Assert.AreEqual(2, blockListItem.Content.Properties.Count());
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expectedInvariantContentValue, blockListItem.Content.Value<string>("invariantText"));
|
||||
Assert.AreEqual(expectedVariantContentValue, blockListItem.Content.Value<string>("variantText"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+118
@@ -64,6 +64,63 @@ internal partial class BlockListElementLevelVariationTests
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Validate_Invalid_Properties_Segment_Variant()
|
||||
{
|
||||
var elementType = CreateElementTypeWithValidation(ContentVariation.Segment);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(ContentVariation.Segment, blockListDataType);
|
||||
var blockListValue = BlockListPropertyValue(
|
||||
elementType,
|
||||
Guid.NewGuid(),
|
||||
Guid.NewGuid(),
|
||||
new BlockProperty(
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "Invalid invariant content value" },
|
||||
new() { Alias = "variantText", Value = "Valid content value (Default)", Segment = null },
|
||||
new() { Alias = "variantText", Value = "Invalid content value (S1)", Segment = "s1" },
|
||||
new() { Alias = "variantText", Value = "Valid content value (S2)", Segment = "s2" },
|
||||
},
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
|
||||
new() { Alias = "variantText", Value = "Invalid content value (Default)", Segment = null },
|
||||
new() { Alias = "variantText", Value = "Valid content value (S1)", Segment = "s1" },
|
||||
new() { Alias = "variantText", Value = "Invalid content value (S2)", Segment = "s2" },
|
||||
},
|
||||
null,
|
||||
null));
|
||||
|
||||
var result = await ContentValidationService.ValidatePropertiesAsync(
|
||||
new ContentCreateModel
|
||||
{
|
||||
ContentTypeKey = contentType.Key,
|
||||
Variants =
|
||||
[
|
||||
new VariantModel { Name = "Name", Segment = null },
|
||||
new VariantModel { Name = "Name", Segment = "s1" },
|
||||
new VariantModel { Name = "Name", Segment = "s2" },
|
||||
],
|
||||
Properties =
|
||||
[
|
||||
new PropertyValueModel { Alias = "blocks", Value = JsonSerializer.Serialize(blockListValue) }
|
||||
]
|
||||
},
|
||||
contentType);
|
||||
|
||||
var errors = result.ValidationErrors.ToArray();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(4, errors.Length);
|
||||
Assert.IsTrue(errors.All(error => error.Alias == "blocks" && error.Culture == null && error.Segment == null));
|
||||
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[0].value"));
|
||||
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[2].value"));
|
||||
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[1].value"));
|
||||
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[3].value"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Validate_Invalid_Properties_Nested_Blocks()
|
||||
{
|
||||
@@ -348,6 +405,67 @@ internal partial class BlockListElementLevelVariationTests
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Validate_Missing_Properties_Segment_Variant()
|
||||
{
|
||||
var elementType = CreateElementTypeWithValidation(ContentVariation.Segment);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(ContentVariation.Segment, blockListDataType);
|
||||
var blockListValue = BlockListPropertyValue(
|
||||
elementType,
|
||||
Guid.NewGuid(),
|
||||
Guid.NewGuid(),
|
||||
new BlockProperty(
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
// missing the mandatory "invariantText" (invariant) and "variantText" (for s2)
|
||||
new() { Alias = "variantText", Value = "Valid content value (S1)", Segment = "s1" },
|
||||
},
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
// missing the mandatory "variantText" (for s1)
|
||||
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
|
||||
new() { Alias = "variantText", Value = "Valid settings value in Danish", Segment = "s2" },
|
||||
},
|
||||
null,
|
||||
null));
|
||||
|
||||
// make sure all blocks are exposed
|
||||
blockListValue.Expose =
|
||||
[
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key, Segment = null },
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key, Segment = "s1" },
|
||||
new() { ContentKey = blockListValue.ContentData[0].Key, Segment = "s2" },
|
||||
];
|
||||
|
||||
var result = await ContentValidationService.ValidatePropertiesAsync(
|
||||
new ContentCreateModel
|
||||
{
|
||||
ContentTypeKey = contentType.Key,
|
||||
Variants =
|
||||
[
|
||||
new VariantModel { Name = "Name", Segment = null },
|
||||
new VariantModel { Name = "Name", Segment = "s1" },
|
||||
new VariantModel { Name = "Name", Segment = "s2" },
|
||||
],
|
||||
Properties =
|
||||
[
|
||||
new PropertyValueModel { Alias = "blocks", Value = JsonSerializer.Serialize(blockListValue) }
|
||||
]
|
||||
},
|
||||
contentType);
|
||||
|
||||
var errors = result.ValidationErrors.ToArray();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(3, errors.Length);
|
||||
Assert.IsTrue(errors.All(error => error.Alias == "blocks" && error.Culture == null && error.Segment == null));
|
||||
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[?(@.alias == 'invariantText' && @.culture == null && @.segment == null)].value"));
|
||||
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[?(@.alias == 'variantText' && @.culture == null && @.segment == 's2')].value"));
|
||||
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[?(@.alias == 'variantText' && @.culture == null && @.segment == 's1')].value"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureAllowEditInvariantFromNonDefaultTrue))]
|
||||
public async Task Can_Validate_Missing_Properties_Nested_Blocks_Specific_Culture_Only_With_AllowEditInvariantFromNonDefault()
|
||||
|
||||
Reference in New Issue
Block a user