#17754: Localizing validation attributes

Work Items: 17754

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2011-09-16 15:23:35 -07:00
parent 24605541d3
commit 3cbcf66b19
10 changed files with 179 additions and 46 deletions

View File

@@ -1,39 +0,0 @@
using Orchard.Localization;
namespace Orchard.Comments.Annotations {
public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute {
public RequiredAttribute() {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override string FormatErrorMessage(string name) {
return T("You must provide a {0} in order to comment.", name).Text;
}
}
public class CommentRequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute {
public CommentRequiredAttribute() {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override string FormatErrorMessage(string name) {
return T("You must provide a Comment.", name).Text;
}
}
public class RegularExpressionAttribute : System.ComponentModel.DataAnnotations.RegularExpressionAttribute {
public RegularExpressionAttribute(string pattern) : base(pattern) {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override string FormatErrorMessage(string name) {
return T("The {0} is not valid.", name).Text;
}
}
}

View File

@@ -67,7 +67,6 @@
<Compile Include="Drivers\CommentsContainerPartDriver.cs" />
<Compile Include="Drivers\CommentSettingsPartDriver.cs" />
<Compile Include="Drivers\CommentsPartDriver.cs" />
<Compile Include="Annotations\CommentValidationAttributes.cs" />
<Compile Include="ResourceManifest.cs" />
<Compile Include="Rules\CommentsActions.cs" />
<Compile Include="Rules\CommentsForms.cs" />

View File

@@ -1,21 +1,22 @@
using System.ComponentModel.DataAnnotations;
using Orchard.Comments.Annotations;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Orchard.Comments.ViewModels {
public class CommentsCreateViewModel {
[Annotations.Required]
[Required]
[StringLength(255)]
public string Name { get; set; }
[Annotations.RegularExpression(@"^[^@\s]+@[^@\s]+$")]
[RegularExpression(@"^[^@\s]+@[^@\s]+$")]
[StringLength(255)]
public string Email { get; set; }
[StringLength(245)]
[Annotations.RegularExpression(@"^(http(s)?://)?([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}[\S]+$")]
[DisplayName("Site")]
[RegularExpression(@"^(http(s)?://)?([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}[\S]+$")]
public string SiteName { get; set; }
[CommentRequired]
[Required, DisplayName("Comment")]
public string CommentText { get; set; }
public int CommentedOn { get; set; }

View File

@@ -23,6 +23,7 @@ using Orchard.FileSystems.VirtualPath;
using Orchard.FileSystems.WebSite;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Mvc.DataAnnotations;
using Orchard.Mvc.ViewEngines.Razor;
using Orchard.Mvc.ViewEngines.ThemeAwareness;
using Orchard.Services;
@@ -143,6 +144,10 @@ namespace Orchard.Environment {
//MvcServiceLocator.SetCurrent(hostContainer);
OrchardHostContainerRegistry.RegisterHostContainer(hostContainer);
// Register localized data annotations
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider());
return container;
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Orchard.Localization;
namespace Orchard.Mvc.DataAnnotations {
public class LocalizedModelValidatorProvider : DataAnnotationsModelValidatorProvider {
private static readonly Dictionary<Type, Func<ValidationAttribute, Localizer, ValidationAttribute>> _validationAttributes;
static LocalizedModelValidatorProvider() {
_validationAttributes = new Dictionary<Type, Func<ValidationAttribute, Localizer, ValidationAttribute>> {
{ typeof(RequiredAttribute), (attribute, t) => new LocalizedRequiredAttribute((RequiredAttribute)attribute, t)},
{ typeof(RangeAttribute), (attribute, t) => new LocalizedRangeAttribute((RangeAttribute)attribute, t)},
{ typeof(StringLengthAttribute), (attribute, t) => new LocalizedStringLengthAttribute((StringLengthAttribute)attribute, t)},
{ typeof(RegularExpressionAttribute), (attribute, t) => new LocalizedRegularExpressionAttribute((RegularExpressionAttribute)attribute, t)}
};
}
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) {
var localizedAttributes = new List<Attribute>();
foreach ( var attribute in attributes ) {
Func<ValidationAttribute, Localizer, ValidationAttribute> localizedAttribute;
// overriden messages have their localization in the scope of the class they are applied to
var tContainer = LocalizationUtilities.Resolve(context, metadata.ContainerType.FullName);
// default translations use the attribute's scope, e.g., System.ComponentModel.DataAnnotations.RequiredAttribute
var tProvider = LocalizationUtilities.Resolve(context, attribute.GetType().FullName);
var validationAttribute = attribute as ValidationAttribute;
// substitute the attribute to its localized version if available
if ( _validationAttributes.TryGetValue(attribute.GetType(), out localizedAttribute) ) {
localizedAttributes.Add(localizedAttribute((ValidationAttribute)attribute, tProvider));
}
else {
// try to inject the localizer if it's an unkown validation attribute
if ( validationAttribute != null ) {
var propertyInfo = validationAttribute.GetType().GetProperty("T", typeof(Localizer));
if ( propertyInfo != null ) {
propertyInfo.SetValue(attribute, tProvider, null);
}
}
if ( attribute is DisplayNameAttribute ) {
metadata.DisplayName = tContainer(metadata.DisplayName).Text;
}
localizedAttributes.Add(attribute);
}
}
var result = base.GetValidators(metadata, context, localizedAttributes);
return result;
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using Orchard.Localization;
namespace Orchard.Mvc.DataAnnotations {
public class LocalizedRangeAttribute : RangeAttribute {
public LocalizedRangeAttribute(RangeAttribute attribute, Localizer t)
: base(attribute.OperandType, new FormatterConverter().ToString(attribute.Minimum), new FormatterConverter().ToString(attribute.Maximum)) {
if ( !String.IsNullOrEmpty(attribute.ErrorMessage) )
ErrorMessage = attribute.ErrorMessage;
T = t;
}
public Localizer T { get; set; }
public override string FormatErrorMessage(string name) {
return String.IsNullOrEmpty(ErrorMessage)
? T("The field {0} must be between {1} and {2}.", name, Minimum, Maximum).Text
: T(ErrorMessage, name, Minimum, Maximum).Text;
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.ComponentModel.DataAnnotations;
using Orchard.Localization;
namespace Orchard.Mvc.DataAnnotations {
public class LocalizedRequiredAttribute : RequiredAttribute {
public LocalizedRequiredAttribute(RequiredAttribute attribute, Localizer t) {
AllowEmptyStrings = attribute.AllowEmptyStrings;
if ( !String.IsNullOrEmpty(attribute.ErrorMessage) )
ErrorMessage = attribute.ErrorMessage;
T = t;
}
public Localizer T { get; set; }
public override string FormatErrorMessage(string name) {
return String.IsNullOrEmpty(ErrorMessage)
? T("The field {0} is required.", name).Text
: T(ErrorMessage, name).Text;
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.ComponentModel.DataAnnotations;
using Orchard.Localization;
namespace Orchard.Mvc.DataAnnotations {
public class LocalizedRegularExpressionAttribute : RegularExpressionAttribute {
public LocalizedRegularExpressionAttribute(RegularExpressionAttribute attribute, Localizer t)
: base(attribute.Pattern) {
if ( !String.IsNullOrEmpty(attribute.ErrorMessage) )
ErrorMessage = attribute.ErrorMessage;
T = t;
}
public Localizer T { get; set; }
public override string FormatErrorMessage(string name) {
return String.IsNullOrEmpty(ErrorMessage)
? T("The field {0} must match the regular expression '{1}'.", name, Pattern).Text
: T(ErrorMessage, name, Pattern).Text;
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.ComponentModel.DataAnnotations;
using Orchard.Localization;
namespace Orchard.Mvc.DataAnnotations {
public class LocalizedStringLengthAttribute : StringLengthAttribute {
public LocalizedStringLengthAttribute(StringLengthAttribute attribute, Localizer t)
: base(attribute.MaximumLength) {
if ( !String.IsNullOrEmpty(attribute.ErrorMessage) )
ErrorMessage = attribute.ErrorMessage;
MinimumLength = attribute.MinimumLength;
T = t;
}
public Localizer T { get; set; }
public override string FormatErrorMessage(string name) {
if ( !String.IsNullOrEmpty(ErrorMessage) )
return T(ErrorMessage, name, MaximumLength, MinimumLength).Text;
return MinimumLength > 0
? T("The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.", name, MaximumLength, MinimumLength).Text
: T("The field {0} must be a string with a maximum length of {1}.", name, MaximumLength, MinimumLength).Text;
}
}
}

View File

@@ -230,6 +230,11 @@
<Compile Include="Logging\OrchardLog4netFactory.cs" />
<Compile Include="Logging\OrchardLog4netLogger.cs" />
<Compile Include="Messaging\Services\DefaultMessageManager.cs" />
<Compile Include="Mvc\DataAnnotations\LocalizedReularExpressionAttribute.cs" />
<Compile Include="Mvc\DataAnnotations\LocalizedStringMaxLengthAttribute.cs" />
<Compile Include="Mvc\DataAnnotations\LocalizedRangeAttribute.cs" />
<Compile Include="Mvc\DataAnnotations\LocalizedModelValidatorProvider.cs" />
<Compile Include="Mvc\DataAnnotations\LocalizedRequiredAttribute.cs" />
<Compile Include="Mvc\Extensions\RouteExtension.cs" />
<Compile Include="Mvc\HttpContextWorkContext.cs" />
<Compile Include="Mvc\Extensions\ControllerExtensions.cs" />