V15: Add textbox and text area serverside validation (#18596)

* Add validation to TextOnlyValueEditor

* Fix tests

* Add tests
This commit is contained in:
Mole
2025-03-06 13:19:28 +01:00
committed by GitHub
parent 7b6cef6083
commit 95c892000d
8 changed files with 130 additions and 6 deletions

View File

@@ -125,6 +125,7 @@ Mange hilsner fra Umbraco robotten
<key alias="outOfRangeMultipleItemsMaximum">De %0% enheder givet er større end det tilladte minimum af %1%.</key>
<key alias="invalidStep">Værdien %0% passer ikke med den konfigureret trin værdi af %1% og mindste værdi af %2%.</key>
<key alias="unexpectedRange">Værdien %0% forventes ikke at indeholde et spænd.</key>
<key alias="stringLengthExceeded">Tekststrengen er længere end den tilladte længde på %0% tegn.</key>
<key alias="invalidRange">Værdien %0% forventes at have en værdi der er større end fra værdien.</key>
<key alias="invalidObjectType">Det valgte indhold er af den forkerte type.</key>
<key alias="notOneOfOptions">"Værdien '%0%' er ikke en af de tilgængelige valgmuligheder.</key>

View File

@@ -378,6 +378,7 @@
<key alias="invalidPattern">Value is invalid, it does not match the correct pattern</key>
<key alias="entriesShort"><![CDATA[Minimum %0% entries, requires <strong>%1%</strong> more.]]></key>
<key alias="entriesExceed"><![CDATA[Maximum %0% entries, <strong>%1%</strong> too many.]]></key>
<key alias="stringLengthExceeded">The string length exceeds the maximum length of %0% characters.</key>
<key alias="entriesAreasMismatch">The content amount requirements are not met for one or more areas.</key>
<key alias="invalidMemberGroupName">Invalid member group name</key>
<key alias="invalidUserGroupName">Invalid user group name</key>

View File

@@ -395,6 +395,7 @@
<key alias="invalidRange">The value %0% is not expected to have a to value less than the from value</key>
<key alias="entriesShort"><![CDATA[Minimum %0% entries, requires <strong>%1%</strong> more.]]></key>
<key alias="entriesExceed"><![CDATA[Maximum %0% entries, <strong>%1%</strong> too many.]]></key>
<key alias="stringLengthExceeded">The string length exceeds the maximum length of %0% characters.</key>
<key alias="entriesAreasMismatch">The content amount requirements are not met for one or more areas.</key>
<key alias="invalidMediaType">The chosen media type is invalid.</key>
<key alias="invalidContentType">The chosen content is of invalid type.</key>

View File

@@ -1,8 +1,13 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Validation;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.PropertyEditors;
@@ -12,23 +17,27 @@ namespace Umbraco.Cms.Core.PropertyEditors;
/// </summary>
public class TextOnlyValueEditor : DataValueEditor
{
[Obsolete($"Use the constructor that does not accept {nameof(ILocalizedTextService)}. Will be removed in V15.")]
public TextOnlyValueEditor(
DataEditorAttribute attribute,
ILocalizedTextService localizedTextService,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper)
: this(attribute, shortStringHelper, jsonSerializer, ioHelper)
{
}
: base(shortStringHelper, jsonSerializer, ioHelper, attribute) =>
Validators.Add(new LengthValidator(localizedTextService));
[Obsolete($"Use the constructor that accepts {nameof(ILocalizedTextService)}. Will be removed in V16.")]
public TextOnlyValueEditor(
DataEditorAttribute attribute,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper)
: base(shortStringHelper, jsonSerializer, ioHelper, attribute)
: this(
attribute,
StaticServiceProvider.Instance.GetRequiredService<ILocalizedTextService>(),
shortStringHelper,
jsonSerializer,
ioHelper)
{
}
@@ -65,4 +74,48 @@ public class TextOnlyValueEditor : DataValueEditor
" can only be used with string based property editors");
}
}
/// <summary>
/// A common length validator for both textbox and text area.
/// </summary>
internal class LengthValidator : IValueValidator
{
private readonly ILocalizedTextService _localizedTextService;
public LengthValidator(ILocalizedTextService localizedTextService)
{
_localizedTextService = localizedTextService;
}
public IEnumerable<ValidationResult> Validate(object? value, string? valueType, object? dataTypeConfiguration,
PropertyValidationContext validationContext)
{
int? maxCharacters = dataTypeConfiguration switch
{
TextAreaConfiguration areaConfiguration => areaConfiguration.MaxChars,
TextboxConfiguration textboxConfiguration => textboxConfiguration.MaxChars,
_ => null,
};
if (maxCharacters is null)
{
return [];
}
if (value is string typedValue && typedValue.Length > maxCharacters)
{
return
[
new ValidationResult(
_localizedTextService.Localize(
"validation",
"stringLengthExceeded",
[maxCharacters.ToString()]),
["value'"])
];
}
return [];
}
}
}

View File

@@ -632,6 +632,7 @@ public class VariationTests
var dataValueEditorFactory = Mock.Of<IDataValueEditorFactory>(x
=> x.Create<TextOnlyValueEditor>(It.IsAny<DataEditorAttribute>()) == new TextOnlyValueEditor(
attribute,
Mock.Of<ILocalizedTextService>(),
Mock.Of<IShortStringHelper>(),
new SystemTextJsonSerializer(),
Mock.Of<IIOHelper>()));

View File

@@ -29,6 +29,7 @@ public class DataValueEditorReuseTests
.Setup(m => m.Create<TextOnlyValueEditor>(It.IsAny<DataEditorAttribute>()))
.Returns(() => new TextOnlyValueEditor(
new DataEditorAttribute("a"),
Mock.Of<ILocalizedTextService>(),
Mock.Of<IShortStringHelper>(),
Mock.Of<IJsonSerializer>(),
Mock.Of<IIOHelper>()));

View File

@@ -0,0 +1,66 @@
using System.ComponentModel.DataAnnotations;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models.Validation;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Infrastructure.Serialization;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors.Validators;
[TestFixture]
internal class TextOnlyValueEditorValidatorTests
{
internal enum ConfigurationType
{
TextAreaConfiguration,
TextboxConfiguration,
}
[TestCase(true, ConfigurationType.TextboxConfiguration, null, "123")]
[TestCase(true, ConfigurationType.TextAreaConfiguration, null, "123")]
[TestCase(false, ConfigurationType.TextAreaConfiguration, 2, "123")]
[TestCase(false, ConfigurationType.TextboxConfiguration, 2, "123")]
[TestCase(true, ConfigurationType.TextboxConfiguration, 10, "123")]
[TestCase(true, ConfigurationType.TextAreaConfiguration, 10, "123")]
public void Validates_String_Length(bool shouldSucceed, ConfigurationType configurationType, int? maxChars, string value)
{
var editor = CreateValueEditor();
editor.ConfigurationObject = CreateConfiguration(configurationType, maxChars);
var results = editor.Validate(value, false, null, PropertyValidationContext.Empty());
ValidateResult(shouldSucceed, results);
}
private static object CreateConfiguration(ConfigurationType type, int? maxChars) =>
type switch
{
ConfigurationType.TextboxConfiguration => new TextboxConfiguration { MaxChars = maxChars },
ConfigurationType.TextAreaConfiguration => new TextAreaConfiguration { MaxChars = maxChars },
_ => throw new InvalidOperationException(),
};
private static void ValidateResult(bool succeed, IEnumerable<ValidationResult> result)
{
if (succeed)
{
Assert.IsEmpty(result);
}
else
{
Assert.IsNotEmpty(result);
}
}
private TextOnlyValueEditor CreateValueEditor() =>
new(
new DataEditorAttribute("alias"),
Mock.Of<ILocalizedTextService>(),
Mock.Of<IShortStringHelper>(),
new SystemTextJsonSerializer(),
Mock.Of<IIOHelper>());
}

View File

@@ -279,7 +279,7 @@ public class PropertyValidationServiceTests
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper)
: base(attribute, shortStringHelper, jsonSerializer, ioHelper)
: base(attribute, Mock.Of<ILocalizedTextService>(), shortStringHelper, jsonSerializer, ioHelper)
{
}