[corlib] Remove unused files
[mono.git] / mcs / class / System.Web.Mvc3 / Mvc / HtmlHelper.cs
1 namespace System.Web.Mvc {
2     using System;
3     using System.Collections.Generic;
4     using System.ComponentModel;
5     using System.Diagnostics.CodeAnalysis;
6     using System.Globalization;
7     using System.IO;
8     using System.Linq;
9     using System.Text;
10     using System.Web;
11     using System.Web.Helpers;
12     using System.Web.Mvc.Resources;
13     using System.Web.Routing;
14
15     public class HtmlHelper {
16         public static readonly string ValidationInputCssClassName = "input-validation-error";
17         public static readonly string ValidationInputValidCssClassName = "input-validation-valid";
18         public static readonly string ValidationMessageCssClassName = "field-validation-error";
19         public static readonly string ValidationMessageValidCssClassName = "field-validation-valid";
20         public static readonly string ValidationSummaryCssClassName = "validation-summary-errors";
21         public static readonly string ValidationSummaryValidCssClassName = "validation-summary-valid";
22
23         public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
24             : this(viewContext, viewDataContainer, RouteTable.Routes) {
25         }
26
27         public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) {
28             if (viewContext == null) {
29                 throw new ArgumentNullException("viewContext");
30             }
31             if (viewDataContainer == null) {
32                 throw new ArgumentNullException("viewDataContainer");
33             }
34             if (routeCollection == null) {
35                 throw new ArgumentNullException("routeCollection");
36             }
37
38             ViewContext = viewContext;
39             ViewDataContainer = viewDataContainer;
40             RouteCollection = routeCollection;
41             ClientValidationRuleFactory = (name, metadata) => ModelValidatorProviders.Providers.GetValidators(metadata ?? ModelMetadata.FromStringExpression(name, ViewData), ViewContext).SelectMany(v => v.GetClientValidationRules());
42         }
43
44         public static bool ClientValidationEnabled {
45             get {
46                 return ViewContext.GetClientValidationEnabled();
47             }
48             set {
49                 ViewContext.SetClientValidationEnabled(value);
50             }
51         }
52
53         public static string IdAttributeDotReplacement {
54             get {
55                 return System.Web.WebPages.Html.HtmlHelper.IdAttributeDotReplacement;
56             }
57             set {
58                 System.Web.WebPages.Html.HtmlHelper.IdAttributeDotReplacement = value;
59             }
60         }
61
62         internal Func<string, ModelMetadata, IEnumerable<ModelClientValidationRule>> ClientValidationRuleFactory {
63             get;
64             set;
65         }
66
67         public RouteCollection RouteCollection {
68             get;
69             private set;
70         }
71
72         public static bool UnobtrusiveJavaScriptEnabled {
73             get {
74                 return ViewContext.GetUnobtrusiveJavaScriptEnabled();
75             }
76             set {
77                 ViewContext.SetUnobtrusiveJavaScriptEnabled(value);
78             }
79         }
80
81         public ViewContext ViewContext {
82             get;
83             private set;
84         }
85
86         public ViewDataDictionary ViewData {
87             get {
88                 return ViewDataContainer.ViewData;
89             }
90         }
91
92         public IViewDataContainer ViewDataContainer {
93             get;
94             private set;
95         }
96
97         public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes) {
98             RouteValueDictionary result = new RouteValueDictionary();
99
100             if (htmlAttributes != null) {
101                 foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(htmlAttributes)) {
102                     result.Add(property.Name.Replace('_', '-'), property.GetValue(htmlAttributes));
103                 }
104             }
105
106             return result;
107         }
108
109         public MvcHtmlString AntiForgeryToken() {
110             return AntiForgeryToken(salt: null);
111         }
112
113         public MvcHtmlString AntiForgeryToken(string salt) {
114             return AntiForgeryToken(salt, domain: null, path: null);
115         }
116
117         public MvcHtmlString AntiForgeryToken(string salt, string domain, string path) {
118             //Disabled to compile MVC3 with the newer System.Web.WebPages helpers
119             //return new MvcHtmlString(AntiForgery.GetHtml(ViewContext.HttpContext, salt, domain, path).ToString());
120             return new MvcHtmlString(AntiForgery.GetHtml().ToString());
121         }
122
123         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
124         public string AttributeEncode(string value) {
125             return (!String.IsNullOrEmpty(value)) ? HttpUtility.HtmlAttributeEncode(value) : String.Empty;
126         }
127
128         public string AttributeEncode(object value) {
129             return AttributeEncode(Convert.ToString(value, CultureInfo.InvariantCulture));
130         }
131
132         public void EnableClientValidation() {
133             EnableClientValidation(enabled: true);
134         }
135
136         public void EnableClientValidation(bool enabled) {
137             ViewContext.ClientValidationEnabled = enabled;
138         }
139
140         public void EnableUnobtrusiveJavaScript() {
141             EnableUnobtrusiveJavaScript(enabled: true);
142         }
143
144         public void EnableUnobtrusiveJavaScript(bool enabled) {
145             ViewContext.UnobtrusiveJavaScriptEnabled = enabled;
146         }
147
148         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
149         public string Encode(string value) {
150             return (!String.IsNullOrEmpty(value)) ? HttpUtility.HtmlEncode(value) : String.Empty;
151         }
152
153         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
154         public string Encode(object value) {
155             return value != null ? HttpUtility.HtmlEncode(value) : String.Empty;
156         }
157
158         internal string EvalString(string key) {
159             return Convert.ToString(ViewData.Eval(key), CultureInfo.CurrentCulture);
160         }
161
162         internal bool EvalBoolean(string key) {
163             return Convert.ToBoolean(ViewData.Eval(key), CultureInfo.InvariantCulture);
164         }
165
166         internal static IView FindPartialView(ViewContext viewContext, string partialViewName, ViewEngineCollection viewEngineCollection) {
167             ViewEngineResult result = viewEngineCollection.FindPartialView(viewContext, partialViewName);
168             if (result.View != null) {
169                 return result.View;
170             }
171
172             StringBuilder locationsText = new StringBuilder();
173             foreach (string location in result.SearchedLocations) {
174                 locationsText.AppendLine();
175                 locationsText.Append(location);
176             }
177
178             throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
179                 MvcResources.Common_PartialViewNotFound, partialViewName, locationsText));
180         }
181
182         public static string GenerateIdFromName(string name) {
183             return GenerateIdFromName(name, IdAttributeDotReplacement);
184         }
185
186         public static string GenerateIdFromName(string name, string idAttributeDotReplacement) {
187             if (name == null) {
188                 throw new ArgumentNullException("name");
189             }
190
191             if (idAttributeDotReplacement == null) {
192                 throw new ArgumentNullException("idAttributeDotReplacement");
193             }
194
195             // TagBuilder.CreateSanitizedId returns null for empty strings, return String.Empty instead to avoid breaking change
196             if (name.Length == 0) {
197                 return String.Empty;
198             }
199
200             return TagBuilder.CreateSanitizedId(name, idAttributeDotReplacement);
201         }
202
203         public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
204             return GenerateLink(requestContext, routeCollection, linkText, routeName, actionName, controllerName, null/* protocol */, null/* hostName */, null/* fragment */, routeValues, htmlAttributes);
205         }
206
207         public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
208             return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes, true /* includeImplicitMvcValues */);
209         }
210
211         private static string GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool includeImplicitMvcValues) {
212             string url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
213             TagBuilder tagBuilder = new TagBuilder("a") {
214                 InnerHtml = (!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : String.Empty
215             };
216             tagBuilder.MergeAttributes(htmlAttributes);
217             tagBuilder.MergeAttribute("href", url);
218             return tagBuilder.ToString(TagRenderMode.Normal);
219         }
220
221         public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
222             return GenerateRouteLink(requestContext, routeCollection, linkText, routeName, null/* protocol */, null/* hostName */, null/* fragment */, routeValues, htmlAttributes);
223         }
224
225         public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
226             return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, null /* actionName */, null /* controllerName */, protocol, hostName, fragment, routeValues, htmlAttributes, false /* includeImplicitMvcValues */);
227         }
228
229         public static string GetFormMethodString(FormMethod method) {
230             switch (method) {
231                 case FormMethod.Get:
232                     return "get";
233                 case FormMethod.Post:
234                     return "post";
235                 default:
236                     return "post";
237             }
238         }
239
240         public static string GetInputTypeString(InputType inputType) {
241             switch (inputType) {
242                 case InputType.CheckBox:
243                     return "checkbox";
244                 case InputType.Hidden:
245                     return "hidden";
246                 case InputType.Password:
247                     return "password";
248                 case InputType.Radio:
249                     return "radio";
250                 case InputType.Text:
251                     return "text";
252                 default:
253                     return "text";
254             }
255         }
256
257         internal object GetModelStateValue(string key, Type destinationType) {
258             ModelState modelState;
259             if (ViewData.ModelState.TryGetValue(key, out modelState)) {
260                 if (modelState.Value != null) {
261                     return modelState.Value.ConvertTo(destinationType, null /* culture */);
262                 }
263             }
264             return null;
265         }
266
267         public IDictionary<string, object> GetUnobtrusiveValidationAttributes(string name) {
268             return GetUnobtrusiveValidationAttributes(name, metadata: null);
269         }
270
271         // Only render attributes if unobtrusive client-side validation is enabled, and then only if we've
272         // never rendered validation for a field with this name in this form. Also, if there's no form context,
273         // then we can't render the attributes (we'd have no <form> to attach them to).
274         public IDictionary<string, object> GetUnobtrusiveValidationAttributes(string name, ModelMetadata metadata) {
275             Dictionary<string, object> results = new Dictionary<string, object>();
276
277             // The ordering of these 3 checks (and the early exits) is for performance reasons.
278             if (!ViewContext.UnobtrusiveJavaScriptEnabled) {
279                 return results;
280             }
281
282             FormContext formContext = ViewContext.GetFormContextForClientValidation();
283             if (formContext == null) {
284                 return results;
285             }
286
287             string fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
288             if (formContext.RenderedField(fullName)) {
289                 return results;
290             }
291
292             formContext.RenderedField(fullName, true);
293
294             IEnumerable<ModelClientValidationRule> clientRules = ClientValidationRuleFactory(name, metadata);
295             bool renderedRules = false;
296
297             foreach (ModelClientValidationRule rule in clientRules) {
298                 renderedRules = true;
299                 string ruleName = "data-val-" + rule.ValidationType;
300
301                 ValidateUnobtrusiveValidationRule(rule, results, ruleName);
302
303                 results.Add(ruleName, HttpUtility.HtmlEncode(rule.ErrorMessage ?? String.Empty));
304                 ruleName += "-";
305
306                 foreach (var kvp in rule.ValidationParameters) {
307                     results.Add(ruleName + kvp.Key, kvp.Value ?? String.Empty);
308                 }
309             }
310
311             if (renderedRules) {
312                 results.Add("data-val", "true");
313             }
314
315             return results;
316         }
317
318         public MvcHtmlString HttpMethodOverride(HttpVerbs httpVerb) {
319             string httpMethod;
320             switch (httpVerb) {
321                 case HttpVerbs.Delete:
322                     httpMethod = "DELETE";
323                     break;
324                 case HttpVerbs.Head:
325                     httpMethod = "HEAD";
326                     break;
327                 case HttpVerbs.Put:
328                     httpMethod = "PUT";
329                     break;
330                 default:
331                     throw new ArgumentException(MvcResources.HtmlHelper_InvalidHttpVerb, "httpVerb");
332             }
333
334             return HttpMethodOverride(httpMethod);
335         }
336
337         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
338         public MvcHtmlString HttpMethodOverride(string httpMethod) {
339             if (String.IsNullOrEmpty(httpMethod)) {
340                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "httpMethod");
341             }
342             if (String.Equals(httpMethod, "GET", StringComparison.OrdinalIgnoreCase) ||
343                 String.Equals(httpMethod, "POST", StringComparison.OrdinalIgnoreCase)) {
344                 throw new ArgumentException(MvcResources.HtmlHelper_InvalidHttpMethod, "httpMethod");
345             }
346
347             TagBuilder tagBuilder = new TagBuilder("input");
348             tagBuilder.Attributes["type"] = "hidden";
349             tagBuilder.Attributes["name"] = HttpRequestExtensions.XHttpMethodOverrideKey;
350             tagBuilder.Attributes["value"] = httpMethod;
351
352             return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
353         }
354
355         /// <summary>
356         /// Wraps HTML markup in an IHtmlString, which will enable HTML markup to be
357         /// rendered to the output without getting HTML encoded.
358         /// </summary>
359         /// <param name="value">HTML markup string.</param>
360         /// <returns>An IHtmlString that represents HTML markup.</returns>
361         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
362         public IHtmlString Raw(string value) {
363             return new HtmlString(value);
364         }
365
366         internal virtual void RenderPartialInternal(string partialViewName, ViewDataDictionary viewData, object model, TextWriter writer, ViewEngineCollection viewEngineCollection) {
367             if (String.IsNullOrEmpty(partialViewName)) {
368                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
369             }
370
371             ViewDataDictionary newViewData = null;
372
373             if (model == null) {
374                 if (viewData == null) {
375                     newViewData = new ViewDataDictionary(ViewData);
376                 }
377                 else {
378                     newViewData = new ViewDataDictionary(viewData);
379                 }
380             }
381             else {
382                 if (viewData == null) {
383                     newViewData = new ViewDataDictionary(model);
384                 }
385                 else {
386                     newViewData = new ViewDataDictionary(viewData) { Model = model };
387                 }
388             }
389
390             ViewContext newViewContext = new ViewContext(ViewContext, ViewContext.View, newViewData, ViewContext.TempData, writer);
391             IView view = FindPartialView(newViewContext, partialViewName, viewEngineCollection);
392             view.Render(newViewContext, writer);
393         }
394
395         private static void ValidateUnobtrusiveValidationRule(ModelClientValidationRule rule, Dictionary<string, object> resultsDictionary, string dictionaryKey) {
396             if (String.IsNullOrWhiteSpace(rule.ValidationType)) {
397                 throw new InvalidOperationException(
398                     String.Format(
399                         CultureInfo.CurrentCulture,
400                         MvcResources.HtmlHelper_ValidationTypeCannotBeEmpty,
401                         rule.GetType().FullName
402                     )
403                 );
404             }
405
406             if (resultsDictionary.ContainsKey(dictionaryKey)) {
407                 throw new InvalidOperationException(
408                     String.Format(
409                         CultureInfo.CurrentCulture,
410                         MvcResources.HtmlHelper_ValidationTypeMustBeUnique,
411                         rule.ValidationType
412                     )
413                 );
414             }
415
416             if (rule.ValidationType.Any(c => !Char.IsLower(c))) {
417                 throw new InvalidOperationException(
418                     String.Format(
419                         CultureInfo.CurrentCulture,
420                         MvcResources.HtmlHelper_ValidationTypeMustBeLegal,
421                         rule.ValidationType,
422                         rule.GetType().FullName
423                     )
424                 );
425             }
426
427             foreach (var key in rule.ValidationParameters.Keys) {
428                 if (String.IsNullOrWhiteSpace(key)) {
429                     throw new InvalidOperationException(
430                         String.Format(
431                             CultureInfo.CurrentCulture,
432                             MvcResources.HtmlHelper_ValidationParameterCannotBeEmpty,
433                             rule.GetType().FullName
434                         )
435                     );
436                 }
437
438                 if (!Char.IsLower(key.First()) || key.Any(c => !Char.IsLower(c) && !Char.IsDigit(c))) {
439                     throw new InvalidOperationException(
440                         String.Format(
441                             CultureInfo.CurrentCulture,
442                             MvcResources.HtmlHelper_ValidationParameterMustBeLegal,
443                             key,
444                             rule.GetType().FullName
445                         )
446                     );
447                 }
448             }
449         }
450     }
451 }