1: using System;
2: using System.Diagnostics;
3: using System.Diagnostics.CodeAnalysis;
4: using System.Web.UI;
5: using System.Web.UI.HtmlControls;
6: using System.Web.UI.WebControls;
7: using System.Web.UI.WebControls.Adapters;
8:
9: /// <summary>
10: /// Implements a custom control adapter for the ASP.NET <see cref="Login"/>
11: /// control that produces CSS-friendly HTML.
12: /// </summary>
13: public class LoginAdapter : WebControlAdapter {
14: private Control container;
15:
16: /// <summary>
17: /// Creates the child controls that will be used to render the
18: /// contents of the <see cref="Login"/> control.
19: /// </summary>
20: /// <param name="e">The event arguments.</param>
21: protected override void OnInit(EventArgs e) {
22: base.OnInit(e);
23:
24: Login login = Control as Login;
25: Debug.Assert(login != null, "login != null");
26:
27: login.LoginError += login_LoginError;
28:
29: ITemplate layoutTemplate = login.LayoutTemplate;
30: if (layoutTemplate == null) {
31: layoutTemplate = new DefaultLoginTemplate(login);
32: }
33:
34: container = new Control();
35: layoutTemplate.InstantiateIn(container);
36: login.Controls.Clear();
37: login.Controls.Add(container);
38: }
39:
40: /// <summary>
41: /// Sets the text and check box settings for the user name, password,
42: /// and remember me fields on the login form.
43: /// </summary>
44: /// <param name="e">The event arguments.</param>
45: protected override void OnLoad(EventArgs e) {
46: base.OnLoad(e);
47:
48: if (!Page.IsPostBack) {
49: Login login = Control as Login;
50: Debug.Assert(login != null, "login != null");
51:
52: ITextControl userName = container.FindControl("UserName") as
53: ITextControl;
54: Debug.Assert(userName != null, "userName != null");
55: userName.Text = login.UserName;
56:
57: ITextControl password = container.FindControl("Password") as
58: ITextControl;
59: Debug.Assert(password != null, "password != null");
60: password.Text = login.Password;
61:
62: ICheckBoxControl rememberMe = container.FindControl("RememberMe")
63: as ICheckBoxControl;
64: Debug.Assert(rememberMe != null, "rememberMe != null");
65: rememberMe.Checked = login.RememberMeSet;
66: }
67: }
68:
69: /// <summary>
70: /// Detaches the adapter from the <see cref="Login.LoginError"/> event.
71: /// </summary>
72: /// <param name="e">The event arguments.</param>
73: protected override void OnUnload(EventArgs e) {
74: Login login = Control as Login;
75: Debug.Assert(login != null, "login != null");
76: login.LoginError -= login_LoginError;
77:
78: base.OnUnload(e);
79: }
80:
81: /// <summary>
82: /// Renders the opening tag that encapsulates the <see cref="Login"/>
83: /// control's content.
84: /// </summary>
85: /// <param name="writer">
86: /// The <see cref="HtmlTextWriter"/> object to use to output the
87: /// opening tag for the control.
88: /// </param>
89: protected override void RenderBeginTag(HtmlTextWriter writer) {
90: Login login = Control as Login;
91: Debug.Assert(login != null, "login != null");
92:
93: writer.AddAttribute(HtmlTextWriterAttribute.Id, login.ClientID);
94:
95: if (!login.ControlStyle.IsEmpty) {
96: if (!String.IsNullOrEmpty(login.ControlStyle.CssClass)) {
97: writer.AddAttribute(HtmlTextWriterAttribute.Class,
98: login.ControlStyle.CssClass);
99: }
100:
101: foreach (string key in login.Style.Keys) {
102: writer.AddStyleAttribute(key, login.Style[key]);
103: }
104: }
105:
106: writer.RenderBeginTag(HtmlTextWriterTag.Div);
107: }
108:
109: /// <summary>
110: /// Renders the inner contents of the <see cref="Login"/> control.
111: /// </summary>
112: /// <param name="writer">
113: /// The <see cref="HtmlTextWriter"/> object to use to output the
114: /// control's contents.
115: /// </param>
116: protected override void RenderContents(HtmlTextWriter writer) {
117: container.RenderControl(writer);
118: }
119:
120: /// <summary>
121: /// Renders the closing outer tag for the <see cref="Login"/> control.
122: /// </summary>
123: /// <param name="writer">
124: /// The <see cref="HtmlTextWriter"/> object to use to output the
125: /// control's contents.
126: /// </param>
127: protected override void RenderEndTag(HtmlTextWriter writer) {
128: writer.RenderEndTag();
129: }
130:
131: /// <summary>
132: /// Displays the failure text in the <see cref="Login"/> control's
133: /// output if a login error occurred.
134: /// </summary>
135: /// <param name="sender">The <see cref="Login"/> control.</param>
136: /// <param name="e">The event arguments.</param>
137: private void login_LoginError(object sender, EventArgs e) {
138: Login login = Control as Login;
139: Debug.Assert(login != null, "login != null");
140:
141: ITextControl failureText = container.FindControl("FailureText") as
142: ITextControl;
143: Debug.Assert(failureText != null, "failureText != null");
144: failureText.Text = login.FailureText;
145:
146: Control parent = ((Control)failureText).Parent;
147: if (!parent.Visible) {
148: parent.Visible = true;
149: }
150: }
151:
152: /// <summary>
153: /// Implements the default template for the <see cref="Login"/> control.
154: /// </summary>
155: private class DefaultLoginTemplate : ITemplate {
156: /// <summary>
157: /// The <see cref="Login"/> control that the template is a template
158: /// for.
159: /// </summary>
160: private Login login;
161:
162: /// <summary>
163: /// Constructs a new <see cref="DefaultLoginTemplate"/> object.
164: /// </summary>
165: /// <param name="login">
166: /// The <see cref="Login"/> control that the template is a template
167: /// for.
168: /// </param>
169: public DefaultLoginTemplate(Login login) {
170: this.login = login;
171: }
172:
173: /// <summary>
174: /// Adds the standard <see cref="Login"/> controls to the template
175: /// container.
176: /// </summary>
177: /// <param name="container">
178: /// The parent container that the controls will be added to as
179: /// children.
180: /// </param>
181: public void InstantiateIn(Control container) {
182: CreateTitleControl(container);
183: CreateInstructionControl(container);
184: CreateFailureControl(container);
185: CreateUserInformationFieldSet(container);
186: CreateOptionsFieldSet(container);
187: CreateLoginButton(container);
188: CreateLinks(container);
189: }
190:
191: /// <summary>
192: /// Creates the controls used to render the link to create a new
193: /// user account.
194: /// </summary>
195: /// <param name="hasCreateUserIconUrl">
196: /// True if the icon should be rendered.
197: /// </param>
198: /// <param name="hasCreateUserText">
199: /// True if the link text should be rendered.
200: /// </param>
201: /// <returns>The link.</returns>
202: private Control CreateCreateUserLink(bool hasCreateUserIconUrl,
203: bool hasCreateUserText) {
204: var listItem = new HtmlGenericControl("LI") {
205: EnableViewState = false
206: };
207:
208: var link = new HyperLink() {
209: EnableViewState = false,
210: NavigateUrl = login.CreateUserUrl
211: };
212: link.MergeStyle(login.HyperLinkStyle);
213: listItem.Controls.Add(link);
214:
215: if (hasCreateUserIconUrl) {
216: var image = new Image() {
217: EnableViewState = false,
218: ImageUrl = login.CreateUserIconUrl
219: };
220: link.Controls.Add(image);
221: }
222: if (hasCreateUserText) {
223: var text = new Literal() {
224: Text = login.CreateUserText
225: };
226: link.Controls.Add(text);
227: }
228:
229: return listItem;
230: }
231:
232: /// <summary>
233: /// Creates the controls that are used to render the failure message
234: /// for the <see cref="Login"/> control.
235: /// </summary>
236: /// <param name="container">
237: /// The parent container <see cref="Control"/> that the new control
238: /// is to be added to.
239: /// </param>
240: private void CreateFailureControl(Control container) {
241: var failureTextDiv = new HtmlGenericControl("DIV") {
242: EnableViewState = false,
243: Visible = false
244: };
245: SetCssStyles(failureTextDiv, login.FailureTextStyle);
246: container.Controls.Add(failureTextDiv);
247:
248: var failureText = new Literal {
249: EnableViewState = false,
250: ID = "FailureText"
251: };
252: failureTextDiv.Controls.Add(failureText);
253: }
254:
255: /// <summary>
256: /// Creates the controls used to render the link to navigate to a
257: /// help page for the login form.
258: /// </summary>
259: /// <param name="hasCreateUserIconUrl">
260: /// True if the icon should be rendered.
261: /// </param>
262: /// <param name="hasCreateUserText">
263: /// True if the link text should be rendered.
264: /// </param>
265: /// <returns>The link.</returns>
266: private Control CreateHelpPageLink(bool hasHelpPageIconUrl,
267: bool hasHelpPageText) {
268: var listItem = new HtmlGenericControl("LI") {
269: EnableViewState = false
270: };
271:
272: var link = new HyperLink() {
273: EnableViewState = false,
274: NavigateUrl = login.HelpPageUrl
275: };
276: link.MergeStyle(login.HyperLinkStyle);
277: listItem.Controls.Add(link);
278:
279: if (hasHelpPageIconUrl) {
280: var image = new Image() {
281: EnableViewState = false,
282: ImageUrl = login.HelpPageIconUrl
283: };
284: link.Controls.Add(image);
285: }
286: if (hasHelpPageText) {
287: var text = new Literal() {
288: Text = login.HelpPageText
289: };
290: link.Controls.Add(text);
291: }
292:
293: return listItem;
294: }
295:
296: /// <summary>
297: /// Creates the controls that are used to render the instructions
298: /// for the <see cref="Login"/> control.
299: /// </summary>
300: /// <param name="container">
301: /// The parent container <see cref="Control"/> that the new control
302: /// is to be added to.
303: /// </param>
304: private void CreateInstructionControl(Control container) {
305: var instructions = new HtmlGenericControl("DIV") {
306: EnableViewState = false,
307: InnerHtml = login.InstructionText
308: };
309: SetCssStyles(instructions, login.InstructionTextStyle);
310: container.Controls.Add(instructions);
311: }
312:
313: /// <summary>
314: /// Creates the controls used to render helpful links to other pages.
315: /// </summary>
316: /// <param name="container">
317: /// The parent container <see cref="Control"/> that the new control
318: /// is to be added to.
319: /// </param>
320: private void CreateLinks(Control container) {
321: bool hasCreateUserIconUrl =
322: !String.IsNullOrEmpty(login.CreateUserIconUrl);
323: bool hasCreateUserText =
324: !String.IsNullOrEmpty(login.CreateUserText);
325: bool hasCreateUserLink = hasCreateUserIconUrl || hasCreateUserText;
326: bool hasPasswordRecoveryIconUrl =
327: !String.IsNullOrEmpty(login.PasswordRecoveryIconUrl);
328: bool hasPasswordRecoveryText =
329: !String.IsNullOrEmpty(login.PasswordRecoveryText);
330: bool hasPasswordRecoveryLink = hasPasswordRecoveryIconUrl ||
331: hasPasswordRecoveryText;
332: bool hasHelpPageIconUrl =
333: !String.IsNullOrEmpty(login.HelpPageIconUrl);
334: bool hasHelpPageText = !String.IsNullOrEmpty(login.HelpPageText);
335: bool hasHelpLink = hasHelpPageIconUrl || hasHelpPageText;
336: if (hasCreateUserLink || hasPasswordRecoveryLink || hasHelpLink) {
337: var linkList = new HtmlGenericControl("UL") {
338: EnableViewState = false
339: };
340: container.Controls.Add(linkList);
341:
342: if (hasCreateUserLink) {
343: linkList.Controls.Add(CreateCreateUserLink(
344: hasCreateUserIconUrl, hasCreateUserText));
345: }
346: if (hasPasswordRecoveryLink) {
347: linkList.Controls.Add(CreatePasswordRecoveryLink(
348: hasPasswordRecoveryIconUrl, hasPasswordRecoveryText));
349: }
350: if (hasHelpLink) {
351: linkList.Controls.Add(CreateHelpPageLink(
352: hasHelpPageIconUrl, hasHelpPageText));
353: }
354: }
355: }
356:
357: /// <summary>
358: /// Creates the <see cref="Button"/> control used to initiate the
359: /// login process.
360: /// </summary>
361: /// <param name="container">
362: /// The parent container <see cref="Control"/> that the new control
363: /// is to be added to.
364: /// </param>
365: private void CreateLoginButton(Control container) {
366: var loginButton = new Button {
367: CommandName = "Login",
368: EnableViewState = false,
369: ID = "LoginButton",
370: Text = login.LoginButtonText,
371: ValidationGroup = login.ID
372: };
373: container.Controls.Add(loginButton);
374: }
375:
376: /// <summary>
377: /// Creates the controls that are used to render the
378: /// <FIELDSET> containing authentication options.
379: /// </summary>
380: /// <param name="container">
381: /// The parent container <see cref="Control"/> that the new control
382: /// is to be added to.
383: /// </param>
384: private void CreateOptionsFieldSet(Control container) {
385: var rememberMeFieldSet = new HtmlGenericControl("FIELDSET") {
386: EnableViewState = false,
387: ID = "RememberMeFieldSet"
388: };
389: container.Controls.Add(rememberMeFieldSet);
390:
391: HtmlGenericControl rememberMeLegend =
392: new HtmlGenericControl("LEGEND") {
393: EnableViewState = false,
394: InnerText = "Options"
395: };
396: rememberMeFieldSet.Controls.Add(rememberMeLegend);
397:
398: var rememberMe = new CheckBox {
399: EnableViewState = false,
400: ID = "RememberMe",
401: Text = login.RememberMeText
402: };
403: rememberMe.MergeStyle(login.CheckBoxStyle);
404: rememberMeFieldSet.Controls.Add(rememberMe);
405: }
406:
407: /// <summary>
408: /// Creates the controls used to capture the user's password.
409: /// </summary>
410: /// <param name="container">
411: /// The container <see cref="Control"/> that the password controls
412: /// will be added to.
413: /// </param>
414: private void CreatePasswordControls(Control container) {
415: var passwordLabel = new Label {
416: AssociatedControlID = "Password",
417: EnableViewState = false,
418: ID = "PasswordLabel",
419: Text = login.PasswordLabelText
420: };
421: passwordLabel.MergeStyle(login.LabelStyle);
422: container.Controls.Add(passwordLabel);
423:
424: var password = new TextBox {
425: ID = "Password",
426: TextMode = TextBoxMode.Password
427: };
428: password.MergeStyle(login.TextBoxStyle);
429: container.Controls.Add(password);
430:
431: var passwordValidator = new RequiredFieldValidator {
432: ControlToValidate = "Password",
433: EnableViewState = false,
434: ID = "PasswordRequired",
435: Text = login.PasswordRequiredErrorMessage,
436: ToolTip = login.PasswordRequiredErrorMessage,
437: ValidationGroup = login.ID
438: };
439: passwordValidator.MergeStyle(login.ValidatorTextStyle);
440: container.Controls.Add(passwordValidator);
441: }
442:
443: /// <summary>
444: /// Creates the controls used to render the link to recover a
445: /// lost password.
446: /// </summary>
447: /// <param name="hasCreateUserIconUrl">
448: /// True if the icon should be rendered.
449: /// </param>
450: /// <param name="hasCreateUserText">
451: /// True if the link text should be rendered.
452: /// </param>
453: /// <returns>The link.</returns>
454: private Control CreatePasswordRecoveryLink(
455: bool hasPasswordRecoveryIconUrl, bool hasPasswordRecoveryText) {
456: var listItem = new HtmlGenericControl("LI") {
457: EnableViewState = false
458: };
459:
460: var link = new HyperLink() {
461: EnableViewState = false,
462: NavigateUrl = login.PasswordRecoveryUrl
463: };
464: link.MergeStyle(login.HyperLinkStyle);
465: listItem.Controls.Add(link);
466:
467: if (hasPasswordRecoveryIconUrl) {
468: var image = new Image() {
469: EnableViewState = false,
470: ImageUrl = login.PasswordRecoveryIconUrl
471: };
472: link.Controls.Add(image);
473: }
474: if (hasPasswordRecoveryText) {
475: var text = new Literal() {
476: Text = login.PasswordRecoveryText
477: };
478: link.Controls.Add(text);
479: }
480:
481: return listItem;
482: }
483:
484: /// <summary>
485: /// Creates the controls that are used to render the title of the
486: /// <see cref="Login"/> control.
487: /// </summary>
488: /// <param name="container">
489: /// The parent container <see cref="Control"/> that the new control
490: /// is to be added to.
491: /// </param>
492: private void CreateTitleControl(Control container) {
493: var title = new HtmlGenericControl("H1") {
494: EnableViewState = false,
495: InnerText = login.TitleText
496: };
497: SetCssStyles(title, login.TitleTextStyle);
498: container.Controls.Add(title);
499: }
500:
501: /// <summary>
502: /// Creates the controls that are used to render the user information
503: /// field set for the <see cref="Login"/> control.
504: /// </summary>
505: /// <param name="container">
506: /// The parent container <see cref="Control"/> that the new control
507: /// is to be added to.
508: /// </param>
509: private void CreateUserInformationFieldSet(Control container) {
510: var userInformationFieldSet =
511: new HtmlGenericControl("FIELDSET") {
512: EnableViewState = false,
513: ID = "UserInformationFieldSet"
514: };
515: container.Controls.Add(userInformationFieldSet);
516:
517: var userInformationLegend =
518: new HtmlGenericControl("LEGEND") {
519: EnableViewState = false,
520: InnerText = "User account information"
521: };
522: userInformationFieldSet.Controls.Add(userInformationLegend);
523:
524: CreateUserNameControls(userInformationFieldSet);
525: CreatePasswordControls(userInformationFieldSet);
526: }
527:
528: /// <summary>
529: /// Creates the controls used to capture the user's account name.
530: /// </summary>
531: /// <param name="container">
532: /// The container <see cref="Control"/> that the user name controls
533: /// will be added to.
534: /// </param>
535: private void CreateUserNameControls(Control container) {
536: var userNameLabel = new Label {
537: AssociatedControlID = "UserName",
538: EnableViewState = false,
539: ID = "UserNameLabel",
540: Text = login.UserNameLabelText
541: };
542: userNameLabel.MergeStyle(login.LabelStyle);
543: container.Controls.Add(userNameLabel);
544:
545: var userName = new TextBox {
546: ID = "UserName"
547: };
548: userName.MergeStyle(login.TextBoxStyle);
549: container.Controls.Add(userName);
550:
551: var userNameValidator = new RequiredFieldValidator {
552: ControlToValidate = "UserName",
553: EnableViewState = false,
554: ID = "UserNameRequired",
555: Text = login.UserNameRequiredErrorMessage,
556: ToolTip = login.UserNameRequiredErrorMessage,
557: ValidationGroup = login.ID
558: };
559: userNameValidator.MergeStyle(login.ValidatorTextStyle);
560: container.Controls.Add(userNameValidator);
561: }
562:
563: /// <summary>
564: /// Sets the CSS class name and CSS styles for a
565: /// <see cref="HtmlControl"/>.
566: /// </summary>
567: /// <param name="control">
568: /// The <see cref="HtmlControl"/> control.
569: /// </param>
570: /// <param name="style">
571: /// A <see cref="Style"/> object containing the CSS settings for
572: /// the control.
573: /// </param>
574: private void SetCssStyles(HtmlControl control, Style style) {
575: if (!style.IsEmpty) {
576: if (!String.IsNullOrEmpty(style.CssClass)) {
577: control.Attributes.Add("class", style.CssClass);
578: }
579:
580: var styles = style.GetStyleAttributes(login.Page);
581: foreach (string key in styles.Keys) {
582: control.Style.Add(key, styles[key]);
583: }
584: }
585: }
586: }
587: }