Merge branch 'release/15.0' into v15/dev

# Conflicts:
#	src/Umbraco.Core/Services/DocumentUrlService.cs
This commit is contained in:
nikolajlauridsen
2024-11-04 12:49:38 +01:00
79 changed files with 1938 additions and 288 deletions

View File

@@ -7,18 +7,19 @@ using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Changes;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure.HybridCache.Services;
using Umbraco.Cms.Tests.Common;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Builders.Extensions;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.PropertyEditors;
@@ -63,6 +64,9 @@ public abstract class BlockEditorElementVariationTestBase : UmbracoIntegrationTe
builder.Services.Configure<ContentSettings>(config =>
config.AllowEditInvariantFromNonDefault = TestsRequiringAllowEditInvariantFromNonDefault.Contains(TestContext.CurrentContext.Test.Name));
builder.AddNotificationHandler<ContentTreeChangeNotification, ContentTreeChangeDistributedCacheNotificationHandler>();
builder.Services.AddUnique<IServerMessenger, ContentEventsTests.LocalServerMessenger>();
}
[SetUp]

View File

@@ -1539,4 +1539,332 @@ public partial class BlockListElementLevelVariationTests
validateBlocks?.Invoke(value);
}
}
[Test]
public async Task Can_Publish_Valid_Properties()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var content = CreateContent(contentType, elementType, [], false);
var blockListValue = BlockListPropertyValue(
elementType,
[
(
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid content value in Danish", Culture = "da-DK" }
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Valid value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid value in Danish", Culture = "da-DK" },
},
null,
null
)
)
]
);
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
ContentService.Save(content);
var publishResult = ContentService.Publish(content, ["en-US", "da-DK"]);
Assert.IsTrue(publishResult.Success);
Assert.IsTrue(publishResult.Content.PublishedCultures.Contains("en-US"));
Assert.IsTrue(publishResult.Content.PublishedCultures.Contains("da-DK"));
}
[Test]
public async Task Can_Publish_Valid_Properties_Specific_Culture_Only()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var content = CreateContent(contentType, elementType, [], false);
var blockListValue = BlockListPropertyValue(
elementType,
[
(
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid content value in Danish", Culture = "da-DK" }
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Valid value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid value in Danish", Culture = "da-DK" },
},
null,
null
)
)
]
);
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
ContentService.Save(content);
var publishResult = ContentService.Publish(content, ["en-US"]);
Assert.IsTrue(publishResult.Success);
Assert.IsTrue(publishResult.Content.PublishedCultures.Contains("en-US"));
Assert.IsFalse(publishResult.Content.PublishedCultures.Contains("da-DK"));
}
[Test]
public async Task Can_Publish_Valid_Properties_With_Wildcard_Culture()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var content = CreateContent(contentType, elementType, [], false);
var blockListValue = BlockListPropertyValue(
elementType,
[
(
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid content value in Danish", Culture = "da-DK" }
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Valid value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid value in Danish", Culture = "da-DK" },
},
null,
null
)
)
]
);
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
ContentService.Save(content);
var publishResult = ContentService.Publish(content, ["*"]);
Assert.IsTrue(publishResult.Success);
Assert.IsTrue(publishResult.Content.PublishedCultures.Contains("en-US"));
Assert.IsTrue(publishResult.Content.PublishedCultures.Contains("da-DK"));
}
[TestCase(true)]
[TestCase(false)]
public async Task Cannot_Publish_Invalid_Invariant_Properties(bool invalidSettingsValue)
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var content = CreateContent(contentType, elementType, [], false);
var blockListValue = BlockListPropertyValue(
elementType,
[
(
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = $"{(invalidSettingsValue ? "Valid" : "Invalid")} invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid content value in Danish", Culture = "da-DK" }
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = $"{(invalidSettingsValue ? "Invalid" : "Valid")} invariant settings value" },
new() { Alias = "variantText", Value = "Valid value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid value in Danish", Culture = "da-DK" },
},
null,
null
)
)
]
);
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
ContentService.Save(content);
var publishResult = ContentService.Publish(content, ["en-US", "da-DK"]);
Assert.Multiple(() =>
{
Assert.IsFalse(publishResult.Success);
Assert.IsNotNull(publishResult.InvalidProperties);
Assert.AreEqual(1, publishResult.InvalidProperties.Count());
Assert.AreEqual("blocks", publishResult.InvalidProperties.First().Alias);
});
}
[Test]
public async Task Cannot_Publish_Missing_Invariant_Properties()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var content = CreateContent(contentType, elementType, [], false);
var blockListValue = BlockListPropertyValue(
elementType,
[
(
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid content value in Danish", Culture = "da-DK" }
},
new List<BlockPropertyValue>
{
new() { Alias = "variantText", Value = "Valid value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid value in Danish", Culture = "da-DK" },
},
null,
null
)
)
]
);
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
ContentService.Save(content);
var publishResult = ContentService.Publish(content, ["en-US", "da-DK"]);
Assert.Multiple(() =>
{
Assert.IsFalse(publishResult.Success);
Assert.IsNotNull(publishResult.InvalidProperties);
Assert.AreEqual(1, publishResult.InvalidProperties.Count());
Assert.AreEqual("blocks", publishResult.InvalidProperties.First().Alias);
});
}
[TestCase(true)]
[TestCase(false)]
public async Task Cannot_Publish_Invalid_Variant_Properties(bool invalidSettingsValue)
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var content = CreateContent(contentType, elementType, [], false);
var blockListValue = BlockListPropertyValue(
elementType,
[
(
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = $"{(invalidSettingsValue ? "Valid" : "Invalid")} content value in Danish", Culture = "da-DK" }
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Valid settings value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = $"{(invalidSettingsValue ? "Invalid" : "Valid")} settings value in Danish", Culture = "da-DK" },
},
null,
null
)
)
]
);
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
ContentService.Save(content);
var publishResult = ContentService.Publish(content, ["en-US"]);
Assert.IsTrue(publishResult.Success);
publishResult = ContentService.Publish(content, ["da-DK"]);
Assert.Multiple(() =>
{
Assert.IsFalse(publishResult.Success);
Assert.IsNotNull(publishResult.InvalidProperties);
Assert.AreEqual(1, publishResult.InvalidProperties.Count());
Assert.AreEqual("blocks", publishResult.InvalidProperties.First().Alias);
});
}
[Test]
public async Task Cannot_Publish_Missing_Variant_Properties()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var content = CreateContent(contentType, elementType, [], false);
var blockListValue = BlockListPropertyValue(
elementType,
[
(
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Valid settings value in English", Culture = "en-US" },
},
null,
null
)
)
]
);
// make sure all blocks are exposed
blockListValue.Expose =
[
new() { ContentKey = blockListValue.ContentData[0].Key, Culture = "en-US" },
new() { ContentKey = blockListValue.ContentData[0].Key, Culture = "da-DK" },
];
content.Properties["blocks"]!.SetValue(JsonSerializer.Serialize(blockListValue));
ContentService.Save(content);
var publishResult = ContentService.Publish(content, ["en-US"]);
Assert.IsTrue(publishResult.Success);
publishResult = ContentService.Publish(content, ["da-DK"]);
Assert.Multiple(() =>
{
Assert.IsFalse(publishResult.Success);
Assert.IsNotNull(publishResult.InvalidProperties);
Assert.AreEqual(1, publishResult.InvalidProperties.Count());
Assert.AreEqual("blocks", publishResult.InvalidProperties.First().Alias);
});
}
}

View File

@@ -0,0 +1,515 @@
using NUnit.Framework;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Blocks;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.PropertyEditors;
public partial class BlockListElementLevelVariationTests
{
private IContentValidationService ContentValidationService => GetRequiredService<IContentValidationService>();
[Test]
public async Task Can_Validate_Invalid_Properties()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, 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 in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Invalid content value in Danish", Culture = "da-DK" },
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Invalid settings value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid settings value in Danish", Culture = "da-DK" },
},
null,
null));
var result = await ContentValidationService.ValidatePropertiesAsync(
new ContentCreateModel
{
ContentTypeKey = contentType.Key,
Variants =
[
new VariantModel { Name = "Name en-US", Properties = [], Culture = "en-US", Segment = null },
new VariantModel { Name = "Name da-DK", Properties = [], Culture = "da-DK", Segment = null }
],
InvariantProperties =
[
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[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"));
});
}
[Test]
public async Task Can_Validate_Invalid_Properties_Nested_Blocks()
{
var (rootElementType, nestedElementType) = await CreateElementTypeWithValidationAndNestedBlocksAsync();
var rootBlockListDataType = await CreateBlockListDataType(rootElementType);
var contentType = CreateContentType(ContentVariation.Culture, rootBlockListDataType);
var blockListValue = BlockListPropertyValue(
rootElementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new()
{
Alias = "nestedBlocks",
Value = BlockListPropertyValue(
nestedElementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Invalid nested invariant content value" },
new() { Alias = "variantText", Value = "Valid nested content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Invalid nested content value in Danish", Culture = "da-DK" },
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid nested invariant settings value" },
new() { Alias = "variantText", Value = "Invalid nested settings value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid nested settings value in Danish", Culture = "da-DK" },
},
null,
null))
},
new() { Alias = "invariantText", Value = "Invalid invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Invalid content value in Danish", Culture = "da-DK" },
},
new List<BlockPropertyValue>
{
new()
{
Alias = "nestedBlocks",
Value = BlockListPropertyValue(
nestedElementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid nested invariant content value" },
new() { Alias = "variantText", Value = "Invalid nested content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid nested content value in Danish", Culture = "da-DK" },
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Invalid nested invariant settings value" },
new() { Alias = "variantText", Value = "Valid nested settings value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Invalid nested settings value in Danish", Culture = "da-DK" },
},
null,
null))
},
new() { Alias = "invariantText", Value = "Valid invariant content value" },
new() { Alias = "variantText", Value = "Invalid content value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid content value in Danish", Culture = "da-DK" },
},
null,
null));
var result = await ContentValidationService.ValidatePropertiesAsync(
new ContentCreateModel
{
ContentTypeKey = contentType.Key,
Variants =
[
new VariantModel { Name = "Name en-US", Properties = [], Culture = "en-US", Segment = null },
new VariantModel { Name = "Name da-DK", Properties = [], Culture = "da-DK", Segment = null }
],
InvariantProperties =
[
new PropertyValueModel { Alias = "blocks", Value = JsonSerializer.Serialize(blockListValue) }
]
},
contentType);
var errors = result.ValidationErrors.ToArray();
Assert.Multiple(() =>
{
Assert.AreEqual(9, 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.contentData[0].values[0].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[0].value.contentData[0].values[2].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[0].value.settingsData[0].values[1].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[1].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[3].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[0].value.contentData[0].values[1].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[0].value.settingsData[0].values[0].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[0].value.settingsData[0].values[2].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[2].value"));
});
}
[Test]
public async Task Can_Validate_Invalid_Properties_Specific_Culture_Only()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, 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 in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Invalid content value in Danish", Culture = "da-DK" },
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Invalid settings value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid settings value in Danish", Culture = "da-DK" },
},
null,
null));
var result = await ContentValidationService.ValidatePropertiesAsync(
new ContentCreateModel
{
ContentTypeKey = contentType.Key,
Variants =
[
new VariantModel { Name = "Name en-US", Properties = [], Culture = "en-US", Segment = null },
new VariantModel { Name = "Name da-DK", Properties = [], Culture = "da-DK", Segment = null }
],
InvariantProperties =
[
new PropertyValueModel { Alias = "blocks", Value = JsonSerializer.Serialize(blockListValue) }
]
},
contentType,
new[] { "en-US" });
var errors = result.ValidationErrors.ToArray();
Assert.Multiple(() =>
{
Assert.AreEqual(2, 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 == ".settingsData[0].values[1].value"));
});
}
[Test]
public async Task Can_Validate_Invalid_Properties_With_Wildcard_Culture()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, 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 in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Invalid content value in Danish", Culture = "da-DK" },
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Invalid settings value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid settings value in Danish", Culture = "da-DK" },
},
null,
null));
var result = await ContentValidationService.ValidatePropertiesAsync(
new ContentCreateModel
{
ContentTypeKey = contentType.Key,
Variants =
[
new VariantModel { Name = "Name en-US", Properties = [], Culture = "en-US", Segment = null },
new VariantModel { Name = "Name da-DK", Properties = [], Culture = "da-DK", Segment = null }
],
InvariantProperties =
[
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[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"));
});
}
[Test]
public async Task Can_Validate_Missing_Properties()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var blockListValue = BlockListPropertyValue(
elementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
// missing the mandatory "invariantText" (invariant) and "variantText" (in Danish)
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
},
new List<BlockPropertyValue>
{
// missing the mandatory "variantText" (in English)
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Valid settings value in Danish", Culture = "da-DK" },
},
null,
null));
// make sure all blocks are exposed
blockListValue.Expose =
[
new() { ContentKey = blockListValue.ContentData[0].Key, Culture = "en-US" },
new() { ContentKey = blockListValue.ContentData[0].Key, Culture = "da-DK" },
];
var result = await ContentValidationService.ValidatePropertiesAsync(
new ContentCreateModel
{
ContentTypeKey = contentType.Key,
Variants =
[
new VariantModel { Name = "Name en-US", Properties = [], Culture = "en-US", Segment = null },
new VariantModel { Name = "Name da-DK", Properties = [], Culture = "da-DK", Segment = null }
],
InvariantProperties =
[
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 == 'da-DK' && @.segment == null)].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[?(@.alias == 'variantText' && @.culture == 'en-US' && @.segment == null)].value"));
});
}
[Test]
public async Task Can_Validate_Missing_Properties_Nested_Blocks_Specific_Culture_Only()
{
var (rootElementType, nestedElementType) = await CreateElementTypeWithValidationAndNestedBlocksAsync();
var rootBlockListDataType = await CreateBlockListDataType(rootElementType);
var contentType = CreateContentType(ContentVariation.Culture, rootBlockListDataType);
var nestedContentBlocks = BlockListPropertyValue(
nestedElementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
// missing the mandatory "invariantText" (invariant) and "variantText" (in Danish)
new() { Alias = "variantText", Value = "Valid nested content value in English", Culture = "en-US" },
},
new List<BlockPropertyValue>
{
// missing the mandatory "variantText" (in English)
new() { Alias = "invariantText", Value = "Valid nested invariant settings value" },
new() { Alias = "variantText", Value = "Valid nested settings value in Danish", Culture = "da-DK" },
},
null,
null));
var nestedSettingsBlocks = BlockListPropertyValue(
nestedElementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
// missing the mandatory "variantText" (in English)
new() { Alias = "invariantText", Value = "Valid nested invariant content value" },
new() { Alias = "variantText", Value = "Valid nested content value in Danish", Culture = "da-DK" },
},
new List<BlockPropertyValue>
{
// missing the mandatory "invariantText" (invariant) and "variantText" (in Danish)
new() { Alias = "variantText", Value = "Valid nested settings value in English", Culture = "en-US" },
},
null,
null));
// make sure all nested blocks are exposed
nestedContentBlocks.Expose =
[
new() { ContentKey = nestedContentBlocks.ContentData[0].Key, Culture = "en-US" },
new() { ContentKey = nestedContentBlocks.ContentData[0].Key, Culture = "da-DK" },
];
nestedSettingsBlocks.Expose =
[
new() { ContentKey = nestedSettingsBlocks.ContentData[0].Key, Culture = "en-US" },
new() { ContentKey = nestedSettingsBlocks.ContentData[0].Key, Culture = "da-DK" },
];
var blockListValue = BlockListPropertyValue(
rootElementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new()
{
Alias = "nestedBlocks",
Value = nestedContentBlocks
},
// missing the mandatory "variantText" (in both English and Danish)
new() { Alias = "invariantText", Value = "Valid root invariant content value" }
},
new List<BlockPropertyValue>
{
new()
{
Alias = "nestedBlocks",
Value = nestedSettingsBlocks
},
// missing the mandatory "invariantText"
new() { Alias = "variantText", Value = "Valid root settings value in English", Culture = "en-US" },
new() { Alias = "variantText", Value = "Valid root settings value in Danish", Culture = "da-DK" }
},
null,
null));
// make sure all root blocks are exposed
blockListValue.Expose =
[
new() { ContentKey = blockListValue.ContentData[0].Key, Culture = "en-US" },
new() { ContentKey = blockListValue.ContentData[0].Key, Culture = "da-DK" },
];
var result = await ContentValidationService.ValidatePropertiesAsync(
new ContentCreateModel
{
ContentTypeKey = contentType.Key,
Variants =
[
new VariantModel { Name = "Name en-US", Properties = [], Culture = "en-US", Segment = null },
new VariantModel { Name = "Name da-DK", Properties = [], Culture = "da-DK", Segment = null }
],
InvariantProperties =
[
new PropertyValueModel { Alias = "blocks", Value = JsonSerializer.Serialize(blockListValue) }
]
},
contentType,
new[] { "da-DK" });
var errors = result.ValidationErrors.ToArray();
Assert.Multiple(() =>
{
Assert.AreEqual(6, 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.contentData[0].values[?(@.alias == 'invariantText' && @.culture == null && @.segment == null)].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[0].value.contentData[0].values[?(@.alias == 'variantText' && @.culture == 'da-DK' && @.segment == null)].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".contentData[0].values[?(@.alias == 'variantText' && @.culture == 'da-DK' && @.segment == null)].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[0].value.settingsData[0].values[?(@.alias == 'invariantText' && @.culture == null && @.segment == null)].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[0].value.settingsData[0].values[?(@.alias == 'variantText' && @.culture == 'da-DK' && @.segment == null)].value"));
Assert.IsNotNull(errors.FirstOrDefault(error => error.JsonPath == ".settingsData[0].values[?(@.alias == 'invariantText' && @.culture == null && @.segment == null)].value"));
});
}
[Test]
public async Task Does_Not_Validate_Unexposed_Blocks()
{
var elementType = CreateElementTypeWithValidation();
var blockListDataType = await CreateBlockListDataType(elementType);
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType);
var blockListValue = BlockListPropertyValue(
elementType,
Guid.NewGuid(),
Guid.NewGuid(),
new BlockProperty(
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant content value" },
new() { Alias = "variantText", Value = "Valid content value in English", Culture = "en-US" },
},
new List<BlockPropertyValue>
{
new() { Alias = "invariantText", Value = "Valid invariant settings value" },
new() { Alias = "variantText", Value = "Valid settings value in English", Culture = "en-US" },
},
null,
null));
// only expose the block in English
blockListValue.Expose =
[
new() { ContentKey = blockListValue.ContentData[0].Key, Culture = "en-US" },
];
var result = await ContentValidationService.ValidatePropertiesAsync(
new ContentCreateModel
{
ContentTypeKey = contentType.Key,
Variants =
[
new VariantModel { Name = "Name en-US", Properties = [], Culture = "en-US", Segment = null },
new VariantModel { Name = "Name da-DK", Properties = [], Culture = "da-DK", Segment = null }
],
InvariantProperties =
[
new PropertyValueModel { Alias = "blocks", Value = JsonSerializer.Serialize(blockListValue) }
]
},
contentType,
["da-DK"]);
Assert.IsEmpty(result.ValidationErrors);
}
}

View File

@@ -146,6 +146,63 @@ public partial class BlockListElementLevelVariationTests : BlockEditorElementVar
return GetPublishedContent(content.Key);
}
private IContentType CreateElementTypeWithValidation()
{
var elementType = CreateElementType(ContentVariation.Culture);
foreach (var propertyType in elementType.PropertyTypes)
{
propertyType.Mandatory = true;
propertyType.ValidationRegExp = "^Valid.*$";
}
ContentTypeService.Save(elementType);
return elementType;
}
private async Task<(IContentType RootElementType, IContentType NestedElementType)> CreateElementTypeWithValidationAndNestedBlocksAsync()
{
var nestedElementType = CreateElementTypeWithValidation();
var nestedBlockListDataType = await CreateBlockListDataType(nestedElementType);
var rootElementType = new ContentTypeBuilder()
.WithAlias("myRootElementType")
.WithName("My Root Element Type")
.WithIsElement(true)
.WithContentVariation(ContentVariation.Culture)
.AddPropertyType()
.WithAlias("invariantText")
.WithName("Invariant text")
.WithMandatory(true)
.WithValidationRegExp("^Valid.*$")
.WithDataTypeId(Constants.DataTypes.Textbox)
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
.WithValueStorageType(ValueStorageType.Nvarchar)
.WithVariations(ContentVariation.Nothing)
.Done()
.AddPropertyType()
.WithAlias("variantText")
.WithName("Variant text")
.WithMandatory(true)
.WithValidationRegExp("^Valid.*$")
.WithDataTypeId(Constants.DataTypes.Textbox)
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
.WithValueStorageType(ValueStorageType.Nvarchar)
.WithVariations(ContentVariation.Culture)
.Done()
.AddPropertyType()
.WithAlias("nestedBlocks")
.WithName("Nested blocks")
.WithDataTypeId(nestedBlockListDataType.Id)
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.BlockList)
.WithValueStorageType(ValueStorageType.Ntext)
.WithVariations(ContentVariation.Nothing)
.Done()
.Build();
ContentTypeService.Save(rootElementType);
return (rootElementType, nestedElementType);
}
private class BlockProperty
{
public BlockProperty(IList<BlockPropertyValue> blockContentValues, IList<BlockPropertyValue> blockSettingsValues, string? culture, string? segment)

View File

@@ -482,7 +482,7 @@ public class RichTextElementLevelVariationTests : BlockEditorElementVariationTes
Assert.AreEqual(1, indexValue.Values.Count());
var indexedValue = indexValue.Values.First() as string;
Assert.IsNotNull(indexedValue);
var values = indexedValue.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
var values = indexedValue.Split(Environment.NewLine).Select(s => s.Trim()).Where(s => s.IsNullOrWhiteSpace() is false).ToArray();
Assert.AreEqual(expectedIndexedValues.Length, values.Length);
Assert.IsTrue(values.ContainsAll(expectedIndexedValues));
}
@@ -562,7 +562,7 @@ public class RichTextElementLevelVariationTests : BlockEditorElementVariationTes
Assert.AreEqual(1, indexValue.Values.Count());
var indexedValue = indexValue.Values.First() as string;
Assert.IsNotNull(indexedValue);
var values = indexedValue.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
var values = indexedValue.Split(Environment.NewLine).Select(s => s.Trim()).Where(s => s.IsNullOrWhiteSpace() is false).ToArray();
Assert.AreEqual(expectedIndexedValues.Length, values.Length);
Assert.IsTrue(values.ContainsAll(expectedIndexedValues));
}
@@ -624,7 +624,7 @@ public class RichTextElementLevelVariationTests : BlockEditorElementVariationTes
Assert.AreEqual(1, indexValue.Values.Count());
var indexedValue = indexValue.Values.First() as string;
Assert.IsNotNull(indexedValue);
var values = indexedValue.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
var values = indexedValue.Split(Environment.NewLine).Select(s => s.Trim()).Where(s => s.IsNullOrWhiteSpace() is false).ToArray();
Assert.AreEqual(expectedIndexedValues.Length, values.Length);
Assert.IsTrue(values.ContainsAll(expectedIndexedValues));
}

View File

@@ -102,6 +102,10 @@ public class ContentValidationServiceTests : UmbracoIntegrationTestWithContent
{ "alias": "text", "value": "Valid nested setting text)" }
]
}
],
"expose": [
{ "contentKey": "f36cebfa-d03b-4451-9e60-4bf32c5b1e2f", "culture": null, "segment": null },
{ "contentKey": "b8173e4a-0618-475c-8277-c3c6af68bee6", "culture": null, "segment": null }
]
}
}
@@ -130,6 +134,10 @@ public class ContentValidationServiceTests : UmbracoIntegrationTestWithContent
{ "alias": "text", "value": "Invalid root setting text (ref #3)" }
]
}
],
"expose": [
{ "contentKey": "9addc377-c02c-4db0-88c2-73b933704f7b", "culture": null, "segment": null },
{ "contentKey": "3af93b5b-5e40-4c64-b142-2564309fc4c7", "culture": null, "segment": null }
]
}
"""