2010-07-23 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / BaseValidator.cs
1 //
2 // System.Web.UI.WebControls.BaseValidator
3 //
4 // Authors:
5 //      Chris Toshok (toshok@novell.com)
6 //
7 // (C) 2005-2010 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Web.Configuration;
30 using System.ComponentModel;
31 using System.Drawing;
32 using System.Reflection;
33 using System.Collections;
34 using System.Security.Permissions;
35
36 namespace System.Web.UI.WebControls
37 {
38         // CAS
39         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
40         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41         // attributes
42         [DefaultProperty("ErrorMessage")]
43         [Designer("System.Web.UI.Design.WebControls.BaseValidatorDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
44         public abstract class BaseValidator : Label, IValidator
45         {
46                 bool render_uplevel;
47                 bool valid;
48                 Color forecolor;
49                 bool pre_render_called = false;
50
51                 protected BaseValidator ()
52                 {
53                         this.valid = true;
54                         this.ForeColor = Color.Red;
55                 }
56
57                 // New in NET1.1 sp1
58                 [Browsable(false)]
59                 [EditorBrowsable(EditorBrowsableState.Never)]
60                 public override string AssociatedControlID {
61                         get { return base.AssociatedControlID; }
62                         set { base.AssociatedControlID = value; }
63                 }
64
65                 [Themeable (false)]
66                 [DefaultValue ("")]
67                 public virtual string ValidationGroup {
68                         get { return ViewState.GetString ("ValidationGroup", String.Empty); }
69                         set { ViewState["ValidationGroup"] = value; }
70                 }
71
72                 [Themeable (false)]
73                 [DefaultValue (false)]
74                 public bool SetFocusOnError {
75                         get { return ViewState.GetBool ("SetFocusOnError", false); }
76                         set { ViewState["SetFocusOnError"] = value; }
77                 }
78
79                 /* listed in corcompare */
80                 [MonoTODO("Why override?")]
81                 [PersistenceMode (PersistenceMode.InnerDefaultProperty)]
82                 [DefaultValue ("")]
83                 public override string Text 
84                 {
85                         get { return base.Text; }
86                         set { base.Text = value; }
87                 }
88
89                 [IDReferenceProperty (typeof (Control))]
90                 [Themeable (false)]
91                 [TypeConverter(typeof(System.Web.UI.WebControls.ValidatedControlConverter))]
92                 [DefaultValue("")]
93                 [WebSysDescription ("")]
94                 [WebCategory ("Behavior")]
95                 public string ControlToValidate {
96                         get { return ViewState.GetString ("ControlToValidate", String.Empty); }
97                         set { ViewState ["ControlToValidate"] = value; }
98                 }
99
100                 [Themeable (false)]
101                 [DefaultValue(ValidatorDisplay.Static)]
102                 [WebSysDescription ("")]
103                 [WebCategory ("Appearance")]
104                 public ValidatorDisplay Display {
105                         get { return (ValidatorDisplay)ViewState.GetInt ("Display", (int)ValidatorDisplay.Static); }
106                         set { ViewState ["Display"] = (int)value; }
107                 }
108
109                 [Themeable (false)]
110                 [DefaultValue(true)]
111                 [WebSysDescription ("")]
112                 [WebCategory ("Behavior")]
113                 public bool EnableClientScript {
114                         get { return ViewState.GetBool ("EnableClientScript", true); }
115                         set { ViewState ["EnableClientScript"] = value; }
116                 }
117
118                 public override bool Enabled {
119                         get { return ViewState.GetBool ("BaseValidatorEnabled", true); }
120                         set { ViewState ["BaseValidatorEnabled"] = value; }
121                 }
122
123                 [Localizable (true)]
124                 [DefaultValue("")]
125                 [WebSysDescription ("")]
126                 [WebCategory ("Appearance")]
127                 public string ErrorMessage {
128                         get { return ViewState.GetString ("ErrorMessage", String.Empty); }
129                         set { ViewState ["ErrorMessage"] = value; }
130                 }
131
132                 [DefaultValue(typeof (Color), "Red")]
133                 public override Color ForeColor {
134                         get { return forecolor; }
135                         set {
136                                 forecolor = value;
137                                 base.ForeColor = value;
138                         }
139                 }
140
141                 [Browsable(false)]
142                 [DefaultValue(true)]
143                 [Themeable (false)]
144                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
145                 [WebSysDescription ("")]
146                 [WebCategory ("Misc")]
147                 public bool IsValid {
148                         get { return valid; }
149                         set { valid = value; }
150                 }
151
152                 protected bool PropertiesValid {
153                         get {
154                                 Control control = NamingContainer.FindControl (ControlToValidate);
155                                 if (control == null)
156                                         return false;
157                                 else
158                                         return true;
159                         }
160                 }
161
162                 protected bool RenderUplevel {
163                         get { return render_uplevel; }
164                 }
165
166                 internal bool GetRenderUplevel ()
167                 {
168                         return render_uplevel;
169                 }
170
171                 protected override void AddAttributesToRender (HtmlTextWriter writer)
172                 {
173                         /* if we're rendering uplevel, add our attributes */
174                         if (render_uplevel) {
175                                 /* force an ID here if we weren't assigned one */
176                                 if (ID == null)
177                                         writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID);
178
179                                 if (ControlToValidate != String.Empty)
180                                         RegisterExpandoAttribute (ClientID, "controltovalidate", GetControlRenderID (ControlToValidate));
181
182                                 if (ErrorMessage != String.Empty)
183                                         RegisterExpandoAttribute (ClientID, "errormessage", ErrorMessage, true);
184
185                                 if (ValidationGroup != String.Empty)
186                                         RegisterExpandoAttribute (ClientID, "validationGroup", ValidationGroup, true);
187
188                                 if (SetFocusOnError)
189                                         RegisterExpandoAttribute (ClientID, "focusOnError", "t");
190
191                                 bool enabled = IsEnabled;
192                                 if (!enabled)
193                                         RegisterExpandoAttribute (ClientID, "enabled", "False");
194
195                                 if (enabled && !IsValid)
196                                         RegisterExpandoAttribute (ClientID, "isvalid", "False");
197                                 else {
198                                         if (Display == ValidatorDisplay.Static)
199                                                 writer.AddStyleAttribute ("visibility", "hidden");
200                                         else
201                                                 writer.AddStyleAttribute ("display", "none");
202                                 }
203
204                                 if (Display != ValidatorDisplay.Static)
205                                         RegisterExpandoAttribute (ClientID, "display", Display.ToString ());
206                         }
207
208                         base.AddAttributesToRender (writer);
209                 }
210
211                 internal void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
212                 {
213                         RegisterExpandoAttribute (controlId, attributeName, attributeValue, false);
214                 }
215
216                 internal void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
217                 {
218                         Page page = Page;
219                         if (page.ScriptManager != null)
220                                 page.ScriptManager.RegisterExpandoAttributeExternal (this, controlId, attributeName, attributeValue, encode);
221                         else
222                                 page.ClientScript.RegisterExpandoAttribute (controlId, attributeName, attributeValue, encode);
223                 }
224
225                 protected void CheckControlValidationProperty (string name, string propertyName)
226                 {
227                         Control control = NamingContainer.FindControl (name);
228                         PropertyDescriptor prop = null;
229
230                         if (control == null)
231                                 throw new HttpException (String.Format ("Unable to find control id '{0}'.", name));
232
233                         prop = BaseValidator.GetValidationProperty (control);
234                         if (prop == null)
235                                 throw new HttpException (String.Format ("Unable to find ValidationProperty attribute '{0}' on control '{1}'", propertyName, name));
236                 }
237
238                 protected virtual bool ControlPropertiesValid ()
239                 {
240                         if (ControlToValidate.Length == 0) {
241                                 throw new HttpException (String.Format ("ControlToValidate property of '{0}' cannot be blank.", ID));
242                         }
243
244                         CheckControlValidationProperty (ControlToValidate, String.Empty);
245
246                         return true;
247                 }
248
249                 protected virtual bool DetermineRenderUplevel ()
250                 {
251                         if (!EnableClientScript)
252                                 return false;
253 #if TARGET_J2EE
254                         if (HttpContext.Current == null)
255                                 return false;
256
257                         return (
258                                 /* From someplace on the web: "JavaScript 1.2
259                                  * and later (also known as ECMAScript) has
260                                  * built-in support for regular
261                                  * expressions" */
262                                 ((Page.Request.Browser.EcmaScriptVersion.Major == 1
263                                   && Page.Request.Browser.EcmaScriptVersion.Minor >= 2)
264                                  || (Page.Request.Browser.EcmaScriptVersion.Major > 1))
265
266                                 /* document.getElementById, .getAttribute,
267                                  * etc, are all DOM level 1.  I don't think we
268                                  * use anything in level 2.. */
269                                 && Page.Request.Browser.W3CDomVersion.Major >= 1);
270 #else
271                         return UplevelHelper.IsUplevel (
272                                 System.Web.Configuration.HttpCapabilitiesBase.GetUserAgentForDetection (HttpContext.Current.Request));
273 #endif
274                 }
275
276                 protected abstract bool EvaluateIsValid ();
277
278                 protected string GetControlRenderID (string name)
279                 {
280                         Control control = NamingContainer.FindControl (name);
281                         if (control == null)
282                                 return null;
283
284                         return control.ClientID;
285                 }
286
287                 protected string GetControlValidationValue (string name)
288                 {
289                         Control control = NamingContainer.FindControl (name);
290
291                         if (control == null)
292                                 return null;
293
294                         PropertyDescriptor prop = BaseValidator.GetValidationProperty (control);
295                         if (prop == null)
296                                 return null;
297
298                         object o = prop.GetValue (control);
299
300                         if (o == null)
301                                 return String.Empty;
302                         
303                         if (o is ListItem)
304                                 return ((ListItem) o).Value;
305                         
306                         return o.ToString ();
307                 }
308
309                 public static PropertyDescriptor GetValidationProperty (object o)
310                 {
311                         PropertyDescriptorCollection props;
312                         System.ComponentModel.AttributeCollection col;
313
314                         props = TypeDescriptor.GetProperties (o);
315                         col = TypeDescriptor.GetAttributes (o);
316
317                         foreach (Attribute at in col) {
318                                 ValidationPropertyAttribute vpa = at as ValidationPropertyAttribute;
319                                 if (vpa != null && vpa.Name != null)
320                                         return props[vpa.Name];
321                         }
322
323                         return null;
324                 }
325
326                 protected internal override void OnInit (EventArgs e)
327                 {
328                         Page page = Page;
329                         /* according to an msdn article, this is done here */
330                         if (page != null) {
331                                 page.Validators.Add (this);
332                                 page.GetValidators (ValidationGroup).Add (this);
333                         }
334                         base.OnInit (e);
335                 }
336
337                 protected internal override void OnPreRender (EventArgs e)
338                 {
339                         base.OnPreRender (e);
340
341                         pre_render_called = true;
342                         
343                         ControlPropertiesValid ();
344
345                         render_uplevel = DetermineRenderUplevel ();
346                         if (render_uplevel)
347                                 RegisterValidatorCommonScript ();
348                 }
349
350                 protected internal override void OnUnload (EventArgs e)
351                 {
352                         Page page = Page;
353                         /* according to an msdn article, this is done here */
354                         if (page != null) {
355                                 page.Validators.Remove (this);
356                                 string validationGroup = ValidationGroup;
357                                 if (!String.IsNullOrEmpty (validationGroup))
358                                         page.GetValidators (ValidationGroup).Remove (this);
359                         }
360                         base.OnUnload (e);
361                 }
362
363                 protected void RegisterValidatorCommonScript ()
364                 {
365                         Page page = Page;
366                         if (page != null) {
367                                 if (page.ScriptManager != null) {
368                                         page.ScriptManager.RegisterClientScriptResourceExternal (this, typeof (BaseValidator), "WebUIValidation_2.0.js");
369                                         page.ScriptManager.RegisterClientScriptBlockExternal (this, typeof (BaseValidator), "ValidationInitializeScript", page.ValidationInitializeScript, true);
370                                         page.ScriptManager.RegisterOnSubmitStatementExternal (this, typeof (BaseValidator), "ValidationOnSubmitStatement", page.ValidationOnSubmitStatement);
371                                         page.ScriptManager.RegisterStartupScriptExternal (this, typeof (BaseValidator), "ValidationStartupScript", page.ValidationStartupScript, true);
372                                 } else if (!page.ClientScript.IsClientScriptIncludeRegistered (typeof (BaseValidator), "Mono-System.Web-ValidationClientScriptBlock")) {
373                                         page.ClientScript.RegisterClientScriptInclude (typeof (BaseValidator), "Mono-System.Web-ValidationClientScriptBlock",
374                                                                                        page.ClientScript.GetWebResourceUrl (typeof (BaseValidator), "WebUIValidation_2.0.js"));
375                                         page.ClientScript.RegisterClientScriptBlock (typeof (BaseValidator), "Mono-System.Web-ValidationClientScriptBlock.Initialize", page.ValidationInitializeScript, true);
376                                         page.ClientScript.RegisterOnSubmitStatement (typeof (BaseValidator), "Mono-System.Web-ValidationOnSubmitStatement", page.ValidationOnSubmitStatement);
377                                         page.ClientScript.RegisterStartupScript (typeof (BaseValidator), "Mono-System.Web-ValidationStartupScript", page.ValidationStartupScript, true);
378                                 }
379                         }
380                 }
381
382                 protected virtual void RegisterValidatorDeclaration ()
383                 {
384                         Page page = Page;
385                         if (page != null) {
386                                 if (page.ScriptManager != null) {
387                                         page.ScriptManager.RegisterArrayDeclarationExternal (this, "Page_Validators", String.Concat ("document.getElementById ('", ClientID, "')"));
388                                         page.ScriptManager.RegisterStartupScriptExternal (this, typeof (BaseValidator), ClientID + "DisposeScript",
389                                                                                           @"
390 document.getElementById('" + ClientID + @"').dispose = function() {
391     Array.remove(Page_Validators, document.getElementById('" + ClientID + @"'));
392 }
393 ", true);
394                                 } else
395                                         page.ClientScript.RegisterArrayDeclaration ("Page_Validators", String.Concat ("document.getElementById ('", ClientID, "')"));
396                         }
397                 }
398
399                 protected internal override void Render (HtmlTextWriter writer)
400                 {
401                         if (!IsEnabled && !EnableClientScript)
402                                 return;
403
404                         if (render_uplevel) {
405                                 /* according to an msdn article, this is done here */
406                                 RegisterValidatorDeclaration ();
407                         }
408
409                         bool render_tags = false;
410                         bool render_text = false;
411                         bool render_nbsp = false;
412                         bool v = IsValid;
413
414                         if (!pre_render_called) {
415                                 render_tags = true;
416                                 render_text = true;
417                         } else if (render_uplevel) {
418                                 render_tags = true;
419                                 render_text = Display != ValidatorDisplay.None;
420                         } else {
421                                 if (Display != ValidatorDisplay.None) {
422                                         render_tags = !v;
423                                         render_text = !v;
424                                         render_nbsp = v && Display == ValidatorDisplay.Static;
425                                 }
426                         }
427
428                         if (render_tags) {
429                                 AddAttributesToRender (writer);
430                                 writer.RenderBeginTag (HtmlTextWriterTag.Span);
431                         }
432
433                         if (render_text || render_nbsp) {
434                                 string text;
435                                 if (render_text) {
436                                         text = Text;
437                                         if (String.IsNullOrEmpty (text))
438                                                 text = ErrorMessage;
439                                 } else
440                                         text = "&nbsp;";
441
442                                 writer.Write (text);
443                         }
444
445
446                         if (render_tags)
447                                 writer.RenderEndTag ();
448                 }
449
450                 /* the docs say "public sealed" here */
451                 public void Validate ()
452                 {
453                         if (IsEnabled && Visible)
454                                 IsValid = ControlPropertiesValid () && EvaluateIsValid ();
455                         else
456                                 IsValid = true;
457                 }
458         }
459 }