Attachment 'patch-login-form-fields-grouped-by-method.diff'
Download 1 # HG changeset patch
2 # User Paul Boddie <paul@boddie.org.uk>
3 # Date 1299526770 -3600
4 # Node ID 45714fde847dbbd79fb2d0189da374cc320c9d9b
5 # Parent 2585f7e2ec044c295d8f825e6717849601fb45a3
6 Changed the login form to group fields by authentication method, showing each
7 method's hint below the fields. Each group of fields is within its own HTML
8 form, so that only relevant fields are submitted.
9 Made the form identifier unique.
10 Added more HTML attributes to the HTML widget classes.
11
12 diff -r 2585f7e2ec04 -r 45714fde847d MoinMoin/userform/login.py
13 --- a/MoinMoin/userform/login.py Sun Mar 06 16:18:22 2011 +0100
14 +++ b/MoinMoin/userform/login.py Mon Mar 07 20:39:30 2011 +0100
15 @@ -4,6 +4,7 @@
16
17 @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
18 2003-2007 MoinMoin:ThomasWaldmann
19 + 2011 Paul Boddie <paul@boddie.org.uk>
20 @license: GNU GPL, see COPYING for details.
21 """
22
23 @@ -28,89 +29,116 @@
24 html.TD().extend(cell),
25 ]))
26
27 -
28 def asHTML(self):
29 """ Create the complete HTML form code. """
30 _ = self._
31 request = self.request
32 + cfg = self.cfg
33 action = "%s%s" % (request.script_root, request.path)
34 - hints = []
35 - for authm in request.cfg.auth:
36 - hint = authm.login_hint(request)
37 - if hint:
38 - hints.append(hint)
39 - self._form = html.FORM(action=action, name="loginform", id="loginform")
40 - self._table = html.TABLE(border="0")
41
42 - # Use the user interface language and direction
43 - lang_attr = request.theme.ui_lang_attr()
44 - self._form.append(html.Raw('<div class="userpref"%s>' % lang_attr))
45 + # Modules should really provide label and type details.
46
47 - self._form.append(html.INPUT(type="hidden", name="action", value="login"))
48 - self._form.append(self._table)
49 - for hint in hints:
50 - self._form.append(html.P().append(html.Raw(hint)))
51 - self._form.append(html.Raw("</div>"))
52 + field_name_mapping = {"username" : "name"}
53 + field_type_mapping = {"password" : "password"}
54 + field_label_mapping = {"username" : _('Name'), "password" : _('Password'), "openid_identifier" : _('OpenID')}
55 + field_id_mapping = {"openid_identifier" : "openididentifier"}
56
57 - cfg = request.cfg
58 - if 'username' in cfg.auth_login_inputs:
59 - self.make_row(_('Name'), [
60 + # Restrict type of input available for certain fields - for example,
61 + # OpenID input - based on wiki configuration.
62 +
63 + field_value_mapping = {"openid_identifier" : cfg.openidrp_allowed_op}
64 +
65 + # Generate a collection of login forms, one per module.
66 +
67 + self._forms = html.DIV()
68 + self._forms.append(html.P().append(html.Text(_('This site supports the following login methods:'))))
69 +
70 + for formnumber, authm in enumerate(cfg.auth):
71 +
72 + # If there are no inputs, don't bother generating a form.
73 +
74 + if not authm.login_inputs:
75 + continue
76 +
77 + self._form = html.FORM(action=action, name="loginform%d" % formnumber)
78 + self._forms.append(self._form)
79 +
80 + # Use the user interface language and direction.
81 +
82 + lang_attr = request.theme.ui_lang_attr()
83 + self._form.append(html.Raw('<div class="userpref"%s>' % lang_attr))
84 +
85 + self._form.append(html.INPUT(type="hidden", name="action", value="login"))
86 +
87 + # Put fields in a table.
88 +
89 + self._table = html.TABLE(border="0", style="table-layout: fixed", width="100%")
90 + self._form.append(self._table)
91 +
92 + # Generate the module's fields.
93 +
94 + for field in authm.login_inputs:
95 + name = field_name_mapping.get(field, field)
96 + type = field_type_mapping.get(field, "text")
97 + label = field_label_mapping.get(field, field)
98 + kw = {}
99 + if field_id_mapping.has_key(field):
100 + kw["id"] = field_id_mapping[field]
101 +
102 + # Restrict type of input based on suggested values.
103 + values = field_value_mapping.get(field, [])
104 +
105 + if len(values) == 1:
106 + # Single values are automatically chosen and hidden.
107 + self.make_row(label, [
108 + html.INPUT(
109 + type="hidden", name=name,
110 + value=values[0]
111 + ),
112 + html.Text(values[0]),
113 + ], width="20%")
114 + elif len(values) > 1:
115 + # Many values are offered as a selection menu.
116 + op_select = html.SELECT(name=name, **kw)
117 + for op_value in values:
118 + op_select.append(html.OPTION(value=op_value).append(
119 + html.Text(op_value)))
120 +
121 + self.make_row(label, [op_select, ], width="20%")
122 + else:
123 + # No suggested values causes a text entry field to appear.
124 + self.make_row(label, [
125 + html.INPUT(
126 + type=type, size="32", name=name, **kw
127 + ),
128 + ], width="20%")
129 +
130 + # Need both hidden field and submit values for auto-submit to work
131 + self.make_row('', [
132 + html.INPUT(type="hidden", name="login", value=_('Login')),
133 html.INPUT(
134 - type="text", size="32", name="name",
135 + type="submit", name='login', value=_('Login')
136 ),
137 ])
138
139 - if 'password' in cfg.auth_login_inputs:
140 - self.make_row(_('Password'), [
141 - html.INPUT(
142 - type="password", size="32", name="password",
143 - ),
144 - ])
145 -
146 - # Restrict type of input available for OpenID input
147 - # based on wiki configuration.
148 - if 'openid_identifier' in cfg.auth_login_inputs:
149 - if len(cfg.openidrp_allowed_op) == 1:
150 - self.make_row(_('OpenID'), [
151 - html.INPUT(
152 - type="hidden", name="openid_identifier",
153 - value=cfg.openidrp_allowed_op[0]
154 - ),
155 - ])
156 - elif len(cfg.openidrp_allowed_op) > 1:
157 - op_select = html.SELECT(name="openid_identifier",
158 - id="openididentifier")
159 - for op_uri in cfg.openidrp_allowed_op:
160 - op_select.append(html.OPTION(value=op_uri).append(
161 - html.Raw(op_uri)))
162 -
163 - self.make_row(_('OpenID'), [op_select, ])
164 - else:
165 - self.make_row(_('OpenID'), [
166 - html.INPUT(
167 - type="text", size="32", name="openid_identifier",
168 - id="openididentifier"
169 - ),
170 - ])
171 -
172 - # Need both hidden field and submit values for auto-submit to work
173 - self.make_row('', [
174 - html.INPUT(type="hidden", name="login", value=_('Login')),
175 - html.INPUT(
176 - type="submit", name='login', value=_('Login')
177 - ),
178 - ])
179 -
180 - # Automatically submit the form if only a single OpenID Provider is allowed
181 - if 'openid_identifier' in cfg.auth_login_inputs and len(cfg.openidrp_allowed_op) == 1:
182 - self._form.append("""<script type="text/javascript">
183 + # Automatically submit the form if only a single OpenID Provider is
184 + # provided
185 + if 'openid_identifier' in authm.login_inputs and len(cfg.openidrp_allowed_op) == 1:
186 + self._form.append("""<script type="text/javascript">
187 <!--//
188 -document.getElementById("loginform").submit();
189 +document.getElementById("loginform%d").submit();
190 //-->
191 </script>
192 -""")
193 +""" % formnumber)
194
195 - return unicode(self._form)
196 + # Finish the form with a hint.
197 +
198 + hint = authm.login_hint(request)
199 + if hint:
200 + self._form.append(html.P().append(html.Raw(hint)))
201 + self._form.append(html.Raw("</div>"))
202 +
203 + return unicode(self._forms)
204
205 def getLogin(request):
206 """ Return HTML code for the login. """
207 diff -r 2585f7e2ec04 -r 45714fde847d MoinMoin/widget/html.py
208 --- a/MoinMoin/widget/html.py Sun Mar 06 16:18:22 2011 +0100
209 +++ b/MoinMoin/widget/html.py Mon Mar 07 20:39:30 2011 +0100
210 @@ -148,6 +148,7 @@
211 'rel': None,
212 'rev': None,
213 'shape': None,
214 + 'style': None,
215 'tabindex': None,
216 'type': None,
217 }
218 @@ -156,18 +157,21 @@
219 "abbreviated form (e.g., WWW, HTTP, etc.)"
220 _ATTRS = {
221 'class': None,
222 + 'style': None,
223 }
224
225 class ACRONYM(CompositeElement):
226 "acronyms"
227 _ATTRS = {
228 'class': None,
229 + 'style': None,
230 }
231
232 class ADDRESS(CompositeElement):
233 "information on author"
234 _ATTRS = {
235 'class': None,
236 + 'style': None,
237 }
238
239 class AREA(EmptyElement):
240 @@ -177,12 +181,14 @@
241 'class': None,
242 'href': None,
243 'shape': None,
244 + 'style': None,
245 }
246
247 class B(CompositeElement):
248 "bold text style"
249 _ATTRS = {
250 'class': None,
251 + 'style': None,
252 }
253
254 class BASE(EmptyElement):
255 @@ -194,18 +200,21 @@
256 "I18N BiDi over-ride"
257 _ATTRS = {
258 'class': None,
259 + 'style': None,
260 }
261
262 class BIG(CompositeElement):
263 "large text style"
264 _ATTRS = {
265 'class': None,
266 + 'style': None,
267 }
268
269 class BLOCKQUOTE(CompositeElement):
270 "long quotation"
271 _ATTRS = {
272 'class': None,
273 + 'style': None,
274 }
275
276 class BODY(CompositeElement):
277 @@ -218,6 +227,7 @@
278 'link': None,
279 'onload': None,
280 'onunload': None,
281 + 'style': None,
282 'text': None,
283 'vlink': None,
284 }
285 @@ -226,48 +236,56 @@
286 "forced line break"
287 _ATTRS = {
288 'class': None,
289 + 'style': None,
290 }
291
292 class BUTTON(CompositeElement):
293 "push button"
294 _ATTRS = {
295 'class': None,
296 + 'style': None,
297 }
298
299 class CAPTION(CompositeElement):
300 "table caption"
301 _ATTRS = {
302 'class': None,
303 + 'style': None,
304 }
305
306 class CITE(CompositeElement):
307 "citation"
308 _ATTRS = {
309 'class': None,
310 + 'style': None,
311 }
312
313 class CODE(CompositeElement):
314 "computer code fragment"
315 _ATTRS = {
316 'class': None,
317 + 'style': None,
318 }
319
320 class DD(CompositeElement):
321 "definition description"
322 _ATTRS = {
323 'class': None,
324 + 'style': None,
325 }
326
327 class DEL(CompositeElement):
328 "deleted text"
329 _ATTRS = {
330 'class': None,
331 + 'style': None,
332 }
333
334 class DFN(CompositeElement):
335 "instance definition"
336 _ATTRS = {
337 'class': None,
338 + 'style': None,
339 }
340
341 class DIV(CompositeElement):
342 @@ -275,24 +293,28 @@
343 _ATTRS = {
344 'id': None,
345 'class': None,
346 + 'style': None,
347 }
348
349 class DL(CompositeElement):
350 "definition list"
351 _ATTRS = {
352 'class': None,
353 + 'style': None,
354 }
355
356 class DT(CompositeElement):
357 "definition term"
358 _ATTRS = {
359 'class': None,
360 + 'style': None,
361 }
362
363 class EM(CompositeElement):
364 "emphasis"
365 _ATTRS = {
366 'class': None,
367 + 'style': None,
368 }
369
370 class FORM(CompositeElement):
371 @@ -307,6 +329,7 @@
372 'name': None,
373 'onreset': None,
374 'onsubmit': None,
375 + 'style': None,
376 'target': None,
377 'id': None,
378 }
379 @@ -318,36 +341,42 @@
380 "heading"
381 _ATTRS = {
382 'class': None,
383 + 'style': None,
384 }
385
386 class H2(CompositeElement):
387 "heading"
388 _ATTRS = {
389 'class': None,
390 + 'style': None,
391 }
392
393 class H3(CompositeElement):
394 "heading"
395 _ATTRS = {
396 'class': None,
397 + 'style': None,
398 }
399
400 class H4(CompositeElement):
401 "heading"
402 _ATTRS = {
403 'class': None,
404 + 'style': None,
405 }
406
407 class H5(CompositeElement):
408 "heading"
409 _ATTRS = {
410 'class': None,
411 + 'style': None,
412 }
413
414 class H6(CompositeElement):
415 "heading"
416 _ATTRS = {
417 'class': None,
418 + 'style': None,
419 }
420
421 class HEAD(CompositeElement):
422 @@ -359,6 +388,7 @@
423 "horizontal rule"
424 _ATTRS = {
425 'class': None,
426 + 'style': None,
427 }
428
429 class HTML(CompositeElement):
430 @@ -371,12 +401,14 @@
431 "italic text style"
432 _ATTRS = {
433 'class': None,
434 + 'style': None,
435 }
436
437 class IFRAME(CompositeElement):
438 "inline subwindow"
439 _ATTRS = {
440 'class': None,
441 + 'style': None,
442 }
443
444 class IMG(EmptyElement):
445 @@ -386,6 +418,7 @@
446 'alt': None,
447 'border': None,
448 'class': None,
449 + 'style': None,
450 'vspace': None,
451 }
452
453 @@ -410,6 +443,7 @@
454 'readonly': None,
455 'size': None,
456 'src': None,
457 + 'style': None,
458 'tabindex': None,
459 'type': None,
460 'usemap': None,
461 @@ -420,12 +454,14 @@
462 "inserted text"
463 _ATTRS = {
464 'class': None,
465 + 'style': None,
466 }
467
468 class KBD(CompositeElement):
469 "text to be entered by the user"
470 _ATTRS = {
471 'class': None,
472 + 'style': None,
473 }
474
475 class LABEL(CompositeElement):
476 @@ -433,6 +469,7 @@
477 _ATTRS = {
478 'class': None,
479 'for_': None,
480 + 'style': None,
481 }
482
483 def _openingtag(self):
484 @@ -456,6 +493,7 @@
485 "list item"
486 _ATTRS = {
487 'class': None,
488 + 'style': None,
489 }
490
491 class LINK(EmptyElement):
492 @@ -468,6 +506,7 @@
493 'media': None,
494 'rel': None,
495 'rev': None,
496 + 'style': None,
497 'target': None,
498 'type': None,
499 }
500 @@ -476,6 +515,7 @@
501 "client-side image map"
502 _ATTRS = {
503 'class': None,
504 + 'style': None,
505 }
506
507 class META(EmptyElement):
508 @@ -487,18 +527,21 @@
509 "alternate content container for non script-based rendering"
510 _ATTRS = {
511 'class': None,
512 + 'style': None,
513 }
514
515 class OL(CompositeElement):
516 "ordered list"
517 _ATTRS = {
518 'class': None,
519 + 'style': None,
520 }
521
522 class OPTGROUP(CompositeElement):
523 "option group"
524 _ATTRS = {
525 'class': None,
526 + 'style': None,
527 }
528
529 class OPTION(CompositeElement):
530 @@ -508,6 +551,7 @@
531 'disabled': None,
532 'label': None,
533 'selected': None,
534 + 'style': None,
535 'value': None,
536 }
537
538 @@ -515,24 +559,28 @@
539 "paragraph"
540 _ATTRS = {
541 'class': None,
542 + 'style': None,
543 }
544
545 class PRE(CompositeElement):
546 "preformatted text"
547 _ATTRS = {
548 'class': None,
549 + 'style': None,
550 }
551
552 class Q(CompositeElement):
553 "short inline quotation"
554 _ATTRS = {
555 'class': None,
556 + 'style': None,
557 }
558
559 class SAMP(CompositeElement):
560 "sample program output, scripts, etc."
561 _ATTRS = {
562 'class': None,
563 + 'style': None,
564 }
565
566 class SCRIPT(CompositeElement):
567 @@ -551,6 +599,7 @@
568 'onchange': None,
569 'onfocus': None,
570 'size': None,
571 + 'style': None,
572 'tabindex': None,
573 'id': None,
574 }
575 @@ -559,18 +608,21 @@
576 "small text style"
577 _ATTRS = {
578 'class': None,
579 + 'style': None,
580 }
581
582 class SPAN(CompositeElement):
583 "generic language/style container"
584 _ATTRS = {
585 'class': None,
586 + 'style': None,
587 }
588
589 class STRONG(CompositeElement):
590 "strong emphasis"
591 _ATTRS = {
592 'class': None,
593 + 'style': None,
594 }
595
596 class STYLE(CompositeElement):
597 @@ -582,12 +634,14 @@
598 "subscript"
599 _ATTRS = {
600 'class': None,
601 + 'style': None,
602 }
603
604 class SUP(CompositeElement):
605 "superscript"
606 _ATTRS = {
607 'class': None,
608 + 'style': None,
609 }
610
611 class TABLE(CompositeElement):
612 @@ -602,6 +656,7 @@
613 'frame': None,
614 'rules': None,
615 'summary': None,
616 + 'style': None,
617 'width': None,
618 }
619
620 @@ -610,6 +665,7 @@
621 _ATTRS = {
622 'align': None,
623 'class': None,
624 + 'style': None,
625 }
626
627 class TD(CompositeElement):
628 @@ -618,7 +674,9 @@
629 'abbr': None,
630 'align': None,
631 'class': None,
632 + 'style': None,
633 'valign': None,
634 + 'width': None,
635 'colspan': None,
636 'rowspan': None,
637 }
638 @@ -630,6 +688,7 @@
639 'cols': None,
640 'name': None,
641 'rows': None,
642 + 'style': None,
643 }
644
645 class TFOOT(CompositeElement):
646 @@ -637,6 +696,7 @@
647 _ATTRS = {
648 'align': None,
649 'class': None,
650 + 'style': None,
651 }
652
653 class TH(CompositeElement):
654 @@ -645,6 +705,8 @@
655 'abbr': None,
656 'align': None,
657 'class': None,
658 + 'style': None,
659 + 'width': None,
660 }
661
662 class THEAD(CompositeElement):
663 @@ -652,6 +714,7 @@
664 _ATTRS = {
665 'align': None,
666 'class': None,
667 + 'style': None,
668 }
669
670 class TITLE(CompositeElement):
671 @@ -664,24 +727,28 @@
672 _ATTRS = {
673 'align': None,
674 'class': None,
675 + 'style': None,
676 }
677
678 class TT(CompositeElement):
679 "teletype or monospaced text style"
680 _ATTRS = {
681 'class': None,
682 + 'style': None,
683 }
684
685 class UL(CompositeElement):
686 "unordered list"
687 _ATTRS = {
688 'class': None,
689 + 'style': None,
690 }
691
692 class VAR(CompositeElement):
693 "instance of a variable or program argument"
694 _ATTRS = {
695 'class': None,
696 + 'style': None,
697 }
698
699
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.