Added custom validation messages to property types: model, dto and migration, update via property type editing, display when content editing.

This commit is contained in:
Andy Butland
2019-06-26 14:37:02 +02:00
parent 56669b5c84
commit a90f0c4070
36 changed files with 254 additions and 66 deletions

View File

@@ -176,6 +176,7 @@ namespace Umbraco.Core.Migrations.Upgrade
To<ConvertTinyMceAndGridMediaUrlsToLocalLink>("{B69B6E8C-A769-4044-A27E-4A4E18D1645A}");
To<RenameUserLoginDtoDateIndex>("{0372A42B-DECF-498D-B4D1-6379E907EB94}");
To<FixContentNuCascade>("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}");
To<AddPropertyTypeValidationMessageColumns>("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}");
//FINAL
}

View File

@@ -0,0 +1,20 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
{
public class AddPropertyTypeValidationMessageColumns : MigrationBase
{
public AddPropertyTypeValidationMessageColumns(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
AddColumnIfNotExists<PropertyTypeDto>(columns, "mandatoryMessage");
AddColumnIfNotExists<PropertyTypeDto>(columns, "validationRegExpMessage");
}
}
}

View File

@@ -25,8 +25,10 @@ namespace Umbraco.Core.Models
private string _propertyEditorAlias;
private ValueStorageType _valueStorageType;
private bool _mandatory;
private string _mandatoryMessage;
private int _sortOrder;
private string _validationRegExp;
private string _validationRegExpMessage;
private ContentVariation _variations;
/// <summary>
@@ -175,7 +177,7 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Gets of sets a value indicating whether a value for this property type is required.
/// Gets or sets a value indicating whether a value for this property type is required.
/// </summary>
[DataMember]
public bool Mandatory
@@ -184,6 +186,16 @@ namespace Umbraco.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _mandatory, nameof(Mandatory));
}
/// <summary>
/// Gets or sets the custom validation message used when a value for this PropertyType is required
/// </summary>
[DataMember]
public string MandatoryMessage
{
get => _mandatoryMessage;
set => SetPropertyValueAndDetectChanges(value, ref _mandatoryMessage, nameof(MandatoryMessage));
}
/// <summary>
/// Gets of sets the sort order of the property type.
/// </summary>
@@ -204,6 +216,16 @@ namespace Umbraco.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _validationRegExp, nameof(ValidationRegExp));
}
/// <summary>
/// Gets or sets the custom validation message used when a pattern for this PropertyType must be matched
/// </summary>
[DataMember]
public string ValidationRegExpMessage
{
get => _validationRegExpMessage;
set => SetPropertyValueAndDetectChanges(value, ref _validationRegExpMessage, nameof(ValidationRegExpMessage));
}
/// <summary>
/// Gets or sets the content variation of the property type.
/// </summary>

View File

@@ -749,7 +749,9 @@ namespace Umbraco.Core.Packaging
Name = property.Element("Name").Value,
Description = (string)property.Element("Description"),
Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false,
MandatoryMessage = property.Element("MandatoryMessage") != null ? (string)property.Element("MandatoryMessage") : string.Empty,
ValidationRegExp = (string)property.Element("Validation"),
ValidationRegExpMessage = property.Element("ValidationRegExpMessage") != null ? (string)property.Element("ValidationRegExpMessage") : string.Empty,
SortOrder = sortOrder
};

View File

@@ -43,10 +43,20 @@ namespace Umbraco.Core.Persistence.Dtos
[Constraint(Default = "0")]
public bool Mandatory { get; set; }
[Column("mandatoryMessage")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(500)]
public string MandatoryMessage { get; set; }
[Column("validationRegExp")]
[NullSetting(NullSetting = NullSettings.Null)]
public string ValidationRegExp { get; set; }
[Column("validationRegExpMessage")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(500)]
public string ValidationRegExpMessage { get; set; }
[Column("Description")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(2000)]

View File

@@ -32,9 +32,15 @@ namespace Umbraco.Core.Persistence.Dtos
[Column("mandatory")]
public bool Mandatory { get; set; }
[Column("mandatoryMessage")]
public string MandatoryMessage { get; set; }
[Column("validationRegExp")]
public string ValidationRegExp { get; set; }
[Column("validationRegExpMessage")]
public string ValidationRegExpMessage { get; set; }
[Column("Description")]
public string Description { get; set; }

View File

@@ -59,8 +59,10 @@ namespace Umbraco.Core.Persistence.Factories
propertyType.Key = typeDto.UniqueId;
propertyType.Name = typeDto.Name;
propertyType.Mandatory = typeDto.Mandatory;
propertyType.MandatoryMessage = typeDto.MandatoryMessage;
propertyType.SortOrder = typeDto.SortOrder;
propertyType.ValidationRegExp = typeDto.ValidationRegExp;
propertyType.ValidationRegExpMessage = typeDto.ValidationRegExpMessage;
propertyType.PropertyGroupId = new Lazy<int>(() => tempGroupDto.Id);
propertyType.CreateDate = createDate;
propertyType.UpdateDate = updateDate;
@@ -123,9 +125,11 @@ namespace Umbraco.Core.Persistence.Factories
DataTypeId = propertyType.DataTypeId,
Description = propertyType.Description,
Mandatory = propertyType.Mandatory,
MandatoryMessage = propertyType.MandatoryMessage,
Name = propertyType.Name,
SortOrder = propertyType.SortOrder,
ValidationRegExp = propertyType.ValidationRegExp,
ValidationRegExpMessage = propertyType.ValidationRegExpMessage,
UniqueId = propertyType.Key,
Variations = (byte)propertyType.Variations
};

View File

@@ -24,9 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.DataTypeId), nameof(PropertyTypeDto.DataTypeId));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Description), nameof(PropertyTypeDto.Description));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Mandatory), nameof(PropertyTypeDto.Mandatory));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.MandatoryMessage), nameof(PropertyTypeDto.MandatoryMessage));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Name), nameof(PropertyTypeDto.Name));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.SortOrder), nameof(PropertyTypeDto.SortOrder));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.ValidationRegExp), nameof(PropertyTypeDto.ValidationRegExp));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.ValidationRegExpMessage), nameof(PropertyTypeDto.ValidationRegExpMessage));
DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.PropertyEditorAlias), nameof(DataTypeDto.EditorAlias));
DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.ValueStorageType), nameof(DataTypeDto.DbType));
}

View File

@@ -293,10 +293,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
Id = dto.Id,
Key = dto.UniqueId,
Mandatory = dto.Mandatory,
MandatoryMessage = dto.MandatoryMessage,
Name = dto.Name,
PropertyGroupId = groupId.HasValue ? new Lazy<int>(() => groupId.Value) : null,
SortOrder = dto.SortOrder,
ValidationRegExp = dto.ValidationRegExp,
ValidationRegExpMessage = dto.ValidationRegExpMessage,
Variations = (ContentVariation)dto.Variations
};
}

View File

@@ -85,7 +85,7 @@ namespace Umbraco.Core.PropertyEditors
public string ValueType { get; set; }
/// <inheritdoc />
public IEnumerable<ValidationResult> Validate(object value, bool required, string format)
public IEnumerable<ValidationResult> Validate(object value, bool required, string requiredMessage, string format, string formatMessage)
{
List<ValidationResult> results = null;
var r = Validators.SelectMany(v => v.Validate(value, ValueType, Configuration)).ToList();
@@ -97,14 +97,14 @@ namespace Umbraco.Core.PropertyEditors
if (required)
{
r = RequiredValidator.ValidateRequired(value, ValueType).ToList();
r = RequiredValidator.ValidateRequired(value, ValueType, requiredMessage).ToList();
if (r.Any()) { if (results == null) results = r; else results.AddRange(r); }
}
var stringValue = value?.ToString();
if (!string.IsNullOrWhiteSpace(format) && !string.IsNullOrWhiteSpace(stringValue))
{
r = FormatValidator.ValidateFormat(value, ValueType, format).ToList();
r = FormatValidator.ValidateFormat(value, ValueType, format, formatMessage).ToList();
if (r.Any()) { if (results == null) results = r; else results.AddRange(r); }
}

View File

@@ -35,12 +35,14 @@ namespace Umbraco.Core.PropertyEditors
bool HideLabel { get; }
/// <summary>
/// Validates a property value.
/// Validates a property value using custom messages.
/// </summary>
/// <param name="value">The property value.</param>
/// <param name="required">A value indicating whether the property value is required.</param>
/// <param name="requiredMessage">A custom validation message to use when the property value is required.</param>
/// <param name="format">A specific format (regex) that the property value must respect.</param>
IEnumerable<ValidationResult> Validate(object value, bool required, string format);
/// <param name="formatMessage">A custom validation message to use when the property value is does not match the specific format (regex).</param>
IEnumerable<ValidationResult> Validate(object value, bool required, string requiredMessage, string format, string formatMessage);
/// <summary>
/// Gets the validators to use to validate the edited value.

View File

@@ -14,11 +14,12 @@ namespace Umbraco.Core.PropertyEditors
/// <param name="value">The value to validate.</param>
/// <param name="valueType">The value type.</param>
/// <param name="format">A format definition.</param>
/// <param name="formatMessage">A custom validation message to use when the property value is does not match the specific format (regex).</param>
/// <returns>Validation results.</returns>
/// <remarks>
/// <para>The <paramref name="format" /> is expected to be a valid regular expression.</para>
/// <para>This is used to validate values against the property type validation regular expression.</para>
/// </remarks>
IEnumerable<ValidationResult> ValidateFormat(object value, string valueType, string format);
IEnumerable<ValidationResult> ValidateFormat(object value, string valueType, string format, string formatMessage);
}
}
}

View File

@@ -13,10 +13,11 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="valueType">The value type.</param>
/// <param name="requiredMessage">A custom validation message to use when the property value is required.</param>
/// <returns>Validation results.</returns>
/// <remarks>
/// <para>This is used to validate values when the property type specifies that a value is required.</para>
/// </remarks>
IEnumerable<ValidationResult> ValidateRequired(object value, string valueType);
IEnumerable<ValidationResult> ValidateRequired(object value, string valueType, string requiredMessage);
}
}
}

View File

@@ -58,17 +58,28 @@ namespace Umbraco.Core.PropertyEditors.Validators
public IEnumerable<ValidationResult> Validate(object value, string valueType, object dataTypeConfiguration)
{
if (_regex == null)
{
throw new InvalidOperationException("The validator has not been configured.");
}
return ValidateFormat(value, valueType, _regex);
return ValidateFormat(value, valueType, _regex, string.Empty);
}
/// <inheritdoc cref="IValueFormatValidator.ValidateFormat"/>
public IEnumerable<ValidationResult> ValidateFormat(object value, string valueType, string format)
public IEnumerable<ValidationResult> ValidateFormat(object value, string valueType, string format, string formatMessage)
{
if (string.IsNullOrWhiteSpace(format)) throw new ArgumentNullOrEmptyException(nameof(format));
if (string.IsNullOrWhiteSpace(format))
{
throw new ArgumentNullOrEmptyException(nameof(format));
}
if (value == null || !new Regex(format).IsMatch(value.ToString()))
yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), new[] { "value" });
{
var message = string.IsNullOrWhiteSpace(formatMessage)
? _textService.Localize("validation", "invalidPattern")
: formatMessage;
yield return new ValidationResult(message, new[] { "value" });
}
}
}
}

View File

@@ -27,28 +27,40 @@ namespace Umbraco.Core.PropertyEditors.Validators
/// <inheritdoc cref="IValueValidator.Validate"/>
public IEnumerable<ValidationResult> Validate(object value, string valueType, object dataTypeConfiguration)
{
return ValidateRequired(value, valueType);
return ValidateRequired(value, valueType, string.Empty);
}
/// <inheritdoc cref="IValueRequiredValidator.ValidateRequired"/>
public IEnumerable<ValidationResult> ValidateRequired(object value, string valueType)
public IEnumerable<ValidationResult> ValidateRequired(object value, string valueType, string requiredMessage)
{
if (value == null)
{
yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), new[] {"value"});
var message = string.IsNullOrWhiteSpace(requiredMessage)
? _textService.Localize("validation", "invalidNull")
: requiredMessage;
yield return new ValidationResult(message, new[] {"value"});
yield break;
}
if (valueType.InvariantEquals(ValueTypes.Json))
{
var message = string.IsNullOrWhiteSpace(requiredMessage)
? _textService.Localize("validation", "invalidEmpty")
: requiredMessage;
if (value.ToString().DetectIsEmptyJson())
yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" });
{
yield return new ValidationResult(message, new[] { "value" });
}
yield break;
}
if (value.ToString().IsNullOrWhiteSpace())
{
yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" });
var message = string.IsNullOrWhiteSpace(requiredMessage)
? _textService.Localize("validation", "invalidEmpty")
: requiredMessage;
yield return new ValidationResult(message, new[] { "value" });
}
}
}

View File

@@ -360,7 +360,9 @@ namespace Umbraco.Core.Services.Implement
new XElement("Definition", definition.Key),
new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name),
new XElement("Mandatory", propertyType.Mandatory.ToString()),
new XElement("MandatoryMessage", propertyType.MandatoryMessage),
new XElement("Validation", propertyType.ValidationRegExp),
new XElement("ValidationRegExpMessage", propertyType.ValidationRegExpMessage),
new XElement("Description", new XCData(propertyType.Description)));
genericProperties.Add(genericProperty);
}
@@ -486,7 +488,9 @@ namespace Umbraco.Core.Services.Implement
new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name),
new XElement("SortOrder", propertyType.SortOrder),
new XElement("Mandatory", propertyType.Mandatory.ToString()),
propertyType.MandatoryMessage != null ? new XElement("MandatoryMessage", propertyType.MandatoryMessage) : null,
propertyType.ValidationRegExp != null ? new XElement("Validation", propertyType.ValidationRegExp) : null,
propertyType.ValidationRegExpMessage != null ? new XElement("ValidationRegExpMessage", propertyType.ValidationRegExpMessage) : null,
propertyType.Description != null ? new XElement("Description", new XCData(propertyType.Description)) : null);
genericProperties.Add(genericProperty);

View File

@@ -133,7 +133,7 @@ namespace Umbraco.Core.Services
var editor = _propertyEditors[propertyType.PropertyEditorAlias];
var configuration = _dataTypeService.GetDataType(propertyType.DataTypeId).Configuration;
var valueEditor = editor.GetValueEditor(configuration);
return !valueEditor.Validate(value, propertyType.Mandatory, propertyType.ValidationRegExp).Any();
return !valueEditor.Validate(value, propertyType.Mandatory, propertyType.MandatoryMessage, propertyType.ValidationRegExp, propertyType.ValidationRegExpMessage).Any();
}
}
}

View File

@@ -240,6 +240,7 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\RenameMediaVersionTable.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\ValueListPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\UmbracoSliderPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_1_0\AddPropertyTypeValidationMessageColumns.cs" />
<Compile Include="Migrations\Upgrade\V_8_1_0\FixContentNuCascade.cs" />
<Compile Include="Migrations\Upgrade\V_8_1_0\RenameUserLoginDtoDateIndex.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_1\ChangeNuCacheJsonFormat.cs" />

View File

@@ -621,7 +621,9 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation()
{
Mandatory = true,
Pattern = "xyz"
MandatoryMessage = "Please enter a value",
Pattern = "xyz",
PatternMessage = "Please match the pattern",
}
};
@@ -634,7 +636,9 @@ namespace Umbraco.Tests.Models.Mapping
Assert.AreEqual(basic.DataTypeId, result.DataTypeId);
Assert.AreEqual(basic.Label, result.Name);
Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory);
Assert.AreEqual(basic.Validation.MandatoryMessage, result.MandatoryMessage);
Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp);
Assert.AreEqual(basic.Validation.PatternMessage, result.ValidationRegExpMessage);
}
[Test]
@@ -655,7 +659,9 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation()
{
Mandatory = true,
Pattern = "xyz"
MandatoryMessage = "Please enter a value",
Pattern = "xyz",
PatternMessage = "Please match the pattern",
}
};
@@ -668,7 +674,9 @@ namespace Umbraco.Tests.Models.Mapping
Assert.AreEqual(basic.DataTypeId, result.DataTypeId);
Assert.AreEqual(basic.Label, result.Name);
Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory);
Assert.AreEqual(basic.Validation.MandatoryMessage, result.MandatoryMessage);
Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp);
Assert.AreEqual(basic.Validation.PatternMessage, result.ValidationRegExpMessage);
}
[Test]
@@ -951,7 +959,7 @@ namespace Umbraco.Tests.Models.Mapping
Name = "Tab 1",
SortOrder = 0,
Inherited = false,
Properties = new []
Properties = new[]
{
new MemberPropertyTypeBasic
{
@@ -965,7 +973,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation
{
Mandatory = false,
Pattern = ""
Pattern = string.Empty
},
SortOrder = 0,
DataTypeId = 555
@@ -1000,7 +1008,7 @@ namespace Umbraco.Tests.Models.Mapping
Name = "Tab 1",
SortOrder = 0,
Inherited = false,
Properties = new []
Properties = new[]
{
new PropertyTypeBasic
{
@@ -1011,7 +1019,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation
{
Mandatory = false,
Pattern = ""
Pattern = string.Empty
},
SortOrder = 0,
DataTypeId = 555
@@ -1052,7 +1060,7 @@ namespace Umbraco.Tests.Models.Mapping
Name = "Tab 1",
SortOrder = 0,
Inherited = false,
Properties = new []
Properties = new[]
{
new PropertyTypeBasic
{
@@ -1063,7 +1071,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation
{
Mandatory = false,
Pattern = ""
Pattern = string.Empty
},
SortOrder = 0,
DataTypeId = 555
@@ -1109,7 +1117,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation
{
Mandatory = false,
Pattern = ""
Pattern = string.Empty
},
SortOrder = 0,
DataTypeId = 555
@@ -1133,7 +1141,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation
{
Mandatory = false,
Pattern = ""
Pattern = string.Empty
},
SortOrder = 0,
DataTypeId = 555
@@ -1187,7 +1195,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation
{
Mandatory = false,
Pattern = ""
Pattern = string.Empty
},
SortOrder = 0,
DataTypeId = 555
@@ -1211,7 +1219,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation
{
Mandatory = false,
Pattern = ""
Pattern = string.Empty
},
SortOrder = 0,
DataTypeId = 555

View File

@@ -501,7 +501,9 @@
property.dataTypeIcon = propertyModel.dataTypeIcon;
property.dataTypeName = propertyModel.dataTypeName;
property.validation.mandatory = propertyModel.validation.mandatory;
property.validation.mandatoryMessage = propertyModel.validation.mandatoryMessage;
property.validation.pattern = propertyModel.validation.pattern;
property.validation.patternMessage = propertyModel.validation.patternMessage;
property.showOnMemberProfile = propertyModel.showOnMemberProfile;
property.memberCanEdit = propertyModel.memberCanEdit;
property.isSensitiveValue = propertyModel.isSensitiveValue;
@@ -592,7 +594,9 @@
propertyState: "init",
validation: {
mandatory: false,
pattern: null
mandatoryMessage: null,
pattern: null,
patternMessage: null
}
};

View File

@@ -494,11 +494,11 @@ input.umb-group-builder__group-sort-value {
font-weight: bold;
font-size: 14px;
color: @ui-action-type;
&:hover{
&:hover {
text-decoration: none;
color:@ui-action-type-hover;
border-color:@ui-action-border-hover;
color: @ui-action-type-hover;
border-color: @ui-action-border-hover;
}
}
@@ -555,7 +555,13 @@ input.umb-group-builder__group-sort-value {
overflow: hidden;
}
.editor-validation-pattern{
.editor-validation-message {
min-width: 100%;
min-height: 25px;
margin-top: 4px;
}
.editor-validation-pattern {
border: 1px solid @gray-7;
margin: 10px 0 0;
padding: 6px;

View File

@@ -528,7 +528,8 @@ input[type="checkbox"][readonly] {
.help-inline {
display: inline-block;
vertical-align: middle;
padding-left: 5px;
padding-top: 4px;
padding-left: 2px;
}
div.help {

View File

@@ -81,38 +81,55 @@
</div>
<div class="umb-control-group clearfix" ng-if="!model.property.locked">
<h5><localize key="validation_validation"></localize></h5>
<label>
<label>
<localize key="validation_fieldIsMandatory"></localize>
</label>
<umb-toggle data-element="validation_mandatory"
checked="model.property.validation.mandatory"
on-click="vm.toggleValidation()"
>
on-click="vm.toggleValidation()">
</umb-toggle>
<label class="mt3">
<textarea class="editor-validation-message"
localize="placeholder"
placeholder="@validation_mandatoryMessage"
ng-model="model.property.validation.mandatoryMessage"
ng-if="model.property.validation.mandatory"
umb-auto-resize
ng-keypress="vm.submitOnEnter($event)">
</textarea>
<label class="mt3">
<localize key="validation_customValidation"></localize>
</label>
<select class="umb-dropdown" ng-options="validationType.name for validationType in vm.validationTypes" ng-model="vm.selectedValidationType" ng-change="vm.changeValidationType(vm.selectedValidationType)">
<option value=""><localize key="validation_noValidation">No validation</localize></option>
</select>
<textarea class="editor-validation-pattern"
localize="placeholder"
placeholder="@validation_validationRegExp"
ng-model="model.property.validation.pattern"
ng-change="vm.changeValidationPattern()"
ng-if="vm.showValidationPattern"
umb-auto-resize
focus-when="{{vm.focusOnPatternField}}"
ng-keypress="vm.submitOnEnter($event)">
</textarea>
localize="placeholder"
placeholder="@validation_validationRegExp"
ng-model="model.property.validation.pattern"
ng-change="vm.changeValidationPattern()"
ng-if="vm.showValidationPattern"
umb-auto-resize
focus-when="{{vm.focusOnPatternField}}"
ng-keypress="vm.submitOnEnter($event)">
</textarea>
<textarea class="editor-validation-message"
localize="placeholder"
placeholder="@validation_validationRegExpMessage"
ng-model="model.property.validation.patternMessage"
ng-if="vm.showValidationPattern"
umb-auto-resize
ng-keypress="vm.submitOnEnter($event)">
</textarea>
</div>
<div class="umb-control-group clearfix" ng-if="model.contentType === 'documentType' && model.contentTypeAllowCultureVariant">

View File

@@ -1,4 +1,4 @@
function textboxController($scope) {
function textboxController($scope, localizationService) {
// macro parameter editor doesn't contains a config object,
// so we create a new one to hold any properties
if (!$scope.model.config) {
@@ -18,6 +18,15 @@ function textboxController($scope) {
}
}
$scope.model.change();
$scope.mandatoryMessage = "";
if ($scope.model.validation.mandatoryMessage) {
$scope.mandatoryMessage = $scope.model.validation.mandatoryMessage;
} else {
localizationService.localize("general_required").then(function (value) {
$scope.mandatoryMessage = value;
});
}
}
angular.module('umbraco').controller("Umbraco.PropertyEditors.textboxController", textboxController);

View File

@@ -7,9 +7,9 @@
ng-trim="false"
ng-keyup="model.change()" />
<span ng-messages="textboxFieldForm.textbox.$error" show-validation-on-submit >
<span ng-messages="textboxFieldForm.textbox.$error" show-validation-on-submit>
<span class="help-inline" ng-message="valServer">{{textboxFieldForm.textbox.errorMsg}}</span>
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" ng-message="required">{{mandatoryMessage}}</span>
</span>
<div class="help" ng-if="model.count >= (model.config.maxChars*.8) && model.count <= model.config.maxChars">

View File

@@ -1951,7 +1951,9 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="validateAsUrl">Validate as a URL</key>
<key alias="enterCustomValidation">...or enter a custom validation</key>
<key alias="fieldIsMandatory">Field is mandatory</key>
<key alias="mandatoryMessage">Enter a custom validation error message (optional)</key>
<key alias="validationRegExp">Enter a regular expression</key>
<key alias="validationRegExpMessage">Enter a custom validation error message (optional)</key>
<key alias="minCount">You need to add at least</key>
<key alias="maxCount">You can only have</key>
<key alias="items">items</key>

View File

@@ -1961,7 +1961,9 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="validateAsUrl">Validate as a URL</key>
<key alias="enterCustomValidation">...or enter a custom validation</key>
<key alias="fieldIsMandatory">Field is mandatory</key>
<key alias="mandatoryMessage">Enter a custom validation error message (optional)</key>
<key alias="validationRegExp">Enter a regular expression</key>
<key alias="validationRegExpMessage">Enter a custom validation error message (optional)</key>
<key alias="minCount">You need to add at least</key>
<key alias="maxCount">You can only have</key>
<key alias="items">items</key>

View File

@@ -167,7 +167,7 @@ namespace Umbraco.Web.Editors.Filters
{
// validate
var valueEditor = editor.GetValueEditor(property.DataType.Configuration);
foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.ValidationRegExp))
foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.IsRequiredMessage, property.ValidationRegExp, property.ValidationRegExpMessage))
{
modelState.AddPropertyError(r, property.Alias, property.Culture);
}

View File

@@ -11,10 +11,17 @@ namespace Umbraco.Web.Models.ContentEditing
internal class ContentPropertyDto : ContentPropertyBasic
{
public IDataType DataType { get; set; }
public string Label { get; set; }
public string Description { get; set; }
public bool IsRequired { get; set; }
public string IsRequiredMessage { get; set; }
public string ValidationRegExp { get; set; }
public string ValidationRegExpMessage { get; set; }
}
}

View File

@@ -11,7 +11,13 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "mandatory")]
public bool Mandatory { get; set; }
[DataMember(Name = "mandatoryMessage")]
public string MandatoryMessage { get; set; }
[DataMember(Name = "pattern")]
public string Pattern { get; set; }
[DataMember(Name = "patternMessage")]
public string PatternMessage { get; set; }
}
}

View File

@@ -42,7 +42,9 @@ namespace Umbraco.Web.Models.Mapping
//add the validation information
dest.Validation.Mandatory = originalProp.PropertyType.Mandatory;
dest.Validation.MandatoryMessage = originalProp.PropertyType.MandatoryMessage;
dest.Validation.Pattern = originalProp.PropertyType.ValidationRegExp;
dest.Validation.PatternMessage = originalProp.PropertyType.ValidationRegExpMessage;
if (dest.PropertyEditor == null)
{

View File

@@ -21,7 +21,9 @@ namespace Umbraco.Web.Models.Mapping
base.Map(property, dest, context);
dest.IsRequired = property.PropertyType.Mandatory;
dest.IsRequiredMessage = property.PropertyType.MandatoryMessage;
dest.ValidationRegExp = property.PropertyType.ValidationRegExp;
dest.ValidationRegExpMessage = property.PropertyType.ValidationRegExpMessage;
dest.Description = property.PropertyType.Description;
dest.Label = property.PropertyType.Name;
dest.DataType = DataTypeService.GetDataType(property.PropertyType.DataTypeId);

View File

@@ -220,7 +220,9 @@ namespace Umbraco.Web.Models.Mapping
target.Name = source.Label;
target.DataTypeId = source.DataTypeId;
target.Mandatory = source.Validation.Mandatory;
target.MandatoryMessage = source.Validation.MandatoryMessage;
target.ValidationRegExp = source.Validation.Pattern;
target.ValidationRegExpMessage = source.Validation.PatternMessage;
target.Variations = source.AllowCultureVariant ? ContentVariation.Culture : ContentVariation.Nothing;
if (source.Id > 0)

View File

@@ -223,7 +223,13 @@ namespace Umbraco.Web.Models.Mapping
Alias = p.Alias,
Description = p.Description,
Editor = p.PropertyEditorAlias,
Validation = new PropertyTypeValidation {Mandatory = p.Mandatory, Pattern = p.ValidationRegExp},
Validation = new PropertyTypeValidation
{
Mandatory = p.Mandatory,
MandatoryMessage = p.MandatoryMessage,
Pattern = p.ValidationRegExp,
PatternMessage = p.ValidationRegExpMessage,
},
Label = p.Name,
View = propertyEditor.GetValueEditor().View,
Config = config,

View File

@@ -313,9 +313,19 @@ namespace Umbraco.Web.PropertyEditors
if (propType.Mandatory)
{
if (propValues[propKey] == null)
yield return new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be null", new[] { propKey });
{
var message = string.IsNullOrWhiteSpace(propType.MandatoryMessage)
? $"'{propType.Name}' cannot be null"
: propType.MandatoryMessage;
yield return new ValidationResult($"Item {(i + 1)}: {message}", 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 });
{
var message = string.IsNullOrWhiteSpace(propType.MandatoryMessage)
? $"'{propType.Name}' cannot be empty"
: propType.MandatoryMessage;
yield return new ValidationResult($"Item{(i + 1)}: {message}", new[] { propKey });
}
}
// Check regex
@@ -325,7 +335,10 @@ namespace Umbraco.Web.PropertyEditors
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 });
var message = string.IsNullOrWhiteSpace(propType.ValidationRegExpMessage)
? $"'{propType.Name}' is invalid, it does not match the correct pattern"
: propType.ValidationRegExpMessage;
yield return new ValidationResult($"Item {(i + 1)}: {message}", new[] { propKey });
}
}
}

View File

@@ -57,7 +57,7 @@ namespace Umbraco.Web.PropertyEditors
private class RequiredJsonValueValidator : IValueRequiredValidator
{
/// <inheritdoc />
public IEnumerable<ValidationResult> ValidateRequired(object value, string valueType)
public IEnumerable<ValidationResult> ValidateRequired(object value, string valueType, string requiredMessage)
{
if (value == null)
{