Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/aspnetwebstack.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Web.Mvc/Html/ValidationExtensions.cs')
-rw-r--r--src/System.Web.Mvc/Html/ValidationExtensions.cs390
1 files changed, 390 insertions, 0 deletions
diff --git a/src/System.Web.Mvc/Html/ValidationExtensions.cs b/src/System.Web.Mvc/Html/ValidationExtensions.cs
new file mode 100644
index 00000000..b225110e
--- /dev/null
+++ b/src/System.Web.Mvc/Html/ValidationExtensions.cs
@@ -0,0 +1,390 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Web.Mvc.Properties;
+using System.Web.Routing;
+
+namespace System.Web.Mvc.Html
+{
+ public static class ValidationExtensions
+ {
+ private const string HiddenListItem = @"<li style=""display:none""></li>";
+ private static string _resourceClassKey;
+
+ public static string ResourceClassKey
+ {
+ get { return _resourceClassKey ?? String.Empty; }
+ set { _resourceClassKey = value; }
+ }
+
+ private static FieldValidationMetadata ApplyFieldValidationMetadata(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string modelName)
+ {
+ FormContext formContext = htmlHelper.ViewContext.FormContext;
+ FieldValidationMetadata fieldMetadata = formContext.GetValidationMetadataForField(modelName, true /* createIfNotFound */);
+
+ // write rules to context object
+ IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext);
+ foreach (ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules()))
+ {
+ fieldMetadata.ValidationRules.Add(rule);
+ }
+
+ return fieldMetadata;
+ }
+
+ private static string GetInvalidPropertyValueResource(HttpContextBase httpContext)
+ {
+ string resourceValue = null;
+ if (!String.IsNullOrEmpty(ResourceClassKey) && (httpContext != null))
+ {
+ // If the user specified a ResourceClassKey try to load the resource they specified.
+ // If the class key is invalid, an exception will be thrown.
+ // If the class key is valid but the resource is not found, it returns null, in which
+ // case it will fall back to the MVC default error message.
+ resourceValue = httpContext.GetGlobalResourceObject(ResourceClassKey, "InvalidPropertyValue", CultureInfo.CurrentUICulture) as string;
+ }
+ return resourceValue ?? MvcResources.Common_ValueNotValidForProperty;
+ }
+
+ private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
+ {
+ if (!String.IsNullOrEmpty(error.ErrorMessage))
+ {
+ return error.ErrorMessage;
+ }
+ if (modelState == null)
+ {
+ return null;
+ }
+
+ string attemptedValue = (modelState.Value != null) ? modelState.Value.AttemptedValue : null;
+ return String.Format(CultureInfo.CurrentCulture, GetInvalidPropertyValueResource(httpContext), attemptedValue);
+ }
+
+ // Validate
+
+ public static void Validate(this HtmlHelper htmlHelper, string modelName)
+ {
+ if (modelName == null)
+ {
+ throw new ArgumentNullException("modelName");
+ }
+
+ ValidateHelper(htmlHelper,
+ ModelMetadata.FromStringExpression(modelName, htmlHelper.ViewContext.ViewData),
+ modelName);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static void ValidateFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
+ {
+ ValidateHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
+ ExpressionHelper.GetExpressionText(expression));
+ }
+
+ private static void ValidateHelper(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression)
+ {
+ FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();
+ if (formContext == null || htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
+ {
+ return; // nothing to do
+ }
+
+ string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
+ ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
+ }
+
+ // ValidationMessage
+
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName)
+ {
+ return ValidationMessage(htmlHelper, modelName, null /* validationMessage */, new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, object htmlAttributes)
+ {
+ return ValidationMessage(htmlHelper, modelName, null /* validationMessage */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", Justification = "'validationMessage' refers to the message that will be rendered by the ValidationMessage helper.")]
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage)
+ {
+ return ValidationMessage(htmlHelper, modelName, validationMessage, new RouteValueDictionary());
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", Justification = "'validationMessage' refers to the message that will be rendered by the ValidationMessage helper.")]
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage, object htmlAttributes)
+ {
+ return ValidationMessage(htmlHelper, modelName, validationMessage, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
+ }
+
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, IDictionary<string, object> htmlAttributes)
+ {
+ return ValidationMessage(htmlHelper, modelName, null /* validationMessage */, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", Justification = "'validationMessage' refers to the message that will be rendered by the ValidationMessage helper.")]
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage, IDictionary<string, object> htmlAttributes)
+ {
+ if (modelName == null)
+ {
+ throw new ArgumentNullException("modelName");
+ }
+
+ return ValidationMessageHelper(htmlHelper,
+ ModelMetadata.FromStringExpression(modelName, htmlHelper.ViewContext.ViewData),
+ modelName,
+ validationMessage,
+ htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
+ {
+ return ValidationMessageFor(htmlHelper, expression, null /* validationMessage */, new RouteValueDictionary());
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage)
+ {
+ return ValidationMessageFor(htmlHelper, expression, validationMessage, new RouteValueDictionary());
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage, object htmlAttributes)
+ {
+ return ValidationMessageFor(htmlHelper, expression, validationMessage, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage, IDictionary<string, object> htmlAttributes)
+ {
+ return ValidationMessageHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
+ ExpressionHelper.GetExpressionText(expression),
+ validationMessage,
+ htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Normalization to lowercase is a common requirement for JavaScript and HTML values")]
+ private static MvcHtmlString ValidationMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes)
+ {
+ string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
+ FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();
+
+ if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName) && formContext == null)
+ {
+ return null;
+ }
+
+ ModelState modelState = htmlHelper.ViewData.ModelState[modelName];
+ ModelErrorCollection modelErrors = (modelState == null) ? null : modelState.Errors;
+ ModelError modelError = (((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0]);
+
+ if (modelError == null && formContext == null)
+ {
+ return null;
+ }
+
+ TagBuilder builder = new TagBuilder("span");
+ builder.MergeAttributes(htmlAttributes);
+ builder.AddCssClass((modelError != null) ? HtmlHelper.ValidationMessageCssClassName : HtmlHelper.ValidationMessageValidCssClassName);
+
+ if (!String.IsNullOrEmpty(validationMessage))
+ {
+ builder.SetInnerText(validationMessage);
+ }
+ else if (modelError != null)
+ {
+ builder.SetInnerText(GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, modelState));
+ }
+
+ if (formContext != null)
+ {
+ bool replaceValidationMessageContents = String.IsNullOrEmpty(validationMessage);
+
+ if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
+ {
+ builder.MergeAttribute("data-valmsg-for", modelName);
+ builder.MergeAttribute("data-valmsg-replace", replaceValidationMessageContents.ToString().ToLowerInvariant());
+ }
+ else
+ {
+ FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
+ // rules will already have been written to the metadata object
+ fieldMetadata.ReplaceValidationMessageContents = replaceValidationMessageContents; // only replace contents if no explicit message was specified
+
+ // client validation always requires an ID
+ builder.GenerateId(modelName + "_validationMessage");
+ fieldMetadata.ValidationMessageId = builder.Attributes["id"];
+ }
+ }
+
+ return builder.ToMvcHtmlString(TagRenderMode.Normal);
+ }
+
+ // ValidationSummary
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper)
+ {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors)
+ {
+ return ValidationSummary(htmlHelper, excludePropertyErrors, null /* message */);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string message)
+ {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */, message, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message)
+ {
+ return ValidationSummary(htmlHelper, excludePropertyErrors, message, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string message, object htmlAttributes)
+ {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */, message, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message, object htmlAttributes)
+ {
+ return ValidationSummary(htmlHelper, excludePropertyErrors, message, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes)
+ {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */, message, htmlAttributes);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message, IDictionary<string, object> htmlAttributes)
+ {
+ if (htmlHelper == null)
+ {
+ throw new ArgumentNullException("htmlHelper");
+ }
+
+ FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();
+ if (htmlHelper.ViewData.ModelState.IsValid)
+ {
+ if (formContext == null)
+ {
+ // No client side validation
+ return null;
+ }
+ // TODO: This isn't really about unobtrusive; can we fix up non-unobtrusive to get rid of this, too?
+ if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled && excludePropertyErrors)
+ {
+ // No client-side updates
+ return null;
+ }
+ }
+
+ string messageSpan;
+ if (!String.IsNullOrEmpty(message))
+ {
+ TagBuilder spanTag = new TagBuilder("span");
+ spanTag.SetInnerText(message);
+ messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
+ }
+ else
+ {
+ messageSpan = null;
+ }
+
+ StringBuilder htmlSummary = new StringBuilder();
+ TagBuilder unorderedList = new TagBuilder("ul");
+
+ IEnumerable<ModelState> modelStates = GetModelStateList(htmlHelper, excludePropertyErrors);
+
+ foreach (ModelState modelState in modelStates)
+ {
+ foreach (ModelError modelError in modelState.Errors)
+ {
+ string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);
+ if (!String.IsNullOrEmpty(errorText))
+ {
+ TagBuilder listItem = new TagBuilder("li");
+ listItem.SetInnerText(errorText);
+ htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
+ }
+ }
+ }
+
+ if (htmlSummary.Length == 0)
+ {
+ htmlSummary.AppendLine(HiddenListItem);
+ }
+
+ unorderedList.InnerHtml = htmlSummary.ToString();
+
+ TagBuilder divBuilder = new TagBuilder("div");
+ divBuilder.MergeAttributes(htmlAttributes);
+ divBuilder.AddCssClass((htmlHelper.ViewData.ModelState.IsValid) ? HtmlHelper.ValidationSummaryValidCssClassName : HtmlHelper.ValidationSummaryCssClassName);
+ divBuilder.InnerHtml = messageSpan + unorderedList.ToString(TagRenderMode.Normal);
+
+ if (formContext != null)
+ {
+ if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
+ {
+ if (!excludePropertyErrors)
+ {
+ // Only put errors in the validation summary if they're supposed to be included there
+ divBuilder.MergeAttribute("data-valmsg-summary", "true");
+ }
+ }
+ else
+ {
+ // client val summaries need an ID
+ divBuilder.GenerateId("validationSummary");
+ formContext.ValidationSummaryId = divBuilder.Attributes["id"];
+ formContext.ReplaceValidationSummary = !excludePropertyErrors;
+ }
+ }
+ return divBuilder.ToMvcHtmlString(TagRenderMode.Normal);
+ }
+
+ // Returns non-null list of model states, which caller will render in order provided.
+ private static IEnumerable<ModelState> GetModelStateList(HtmlHelper htmlHelper, bool excludePropertyErrors)
+ {
+ if (excludePropertyErrors)
+ {
+ ModelState ms;
+ htmlHelper.ViewData.ModelState.TryGetValue(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix, out ms);
+ if (ms != null)
+ {
+ return new ModelState[] { ms };
+ }
+
+ return new ModelState[0];
+ }
+ else
+ {
+ // Sort modelStates to respect the ordering in the metadata.
+ // ModelState doesn't refer to ModelMetadata, but we can correlate via the property name.
+ Dictionary<string, int> ordering = new Dictionary<string, int>();
+
+ var metadata = htmlHelper.ViewData.ModelMetadata;
+ if (metadata != null)
+ {
+ foreach (ModelMetadata m in metadata.Properties)
+ {
+ ordering[m.PropertyName] = m.Order;
+ }
+ }
+
+ return from kv in htmlHelper.ViewData.ModelState
+ let name = kv.Key
+ orderby ordering.GetOrDefault(name, ModelMetadata.DefaultOrder)
+ select kv.Value;
+ }
+ }
+ }
+}