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.
  • [get | view] (2011-05-02 21:45:49, 14.1 KB) [[attachment:patch-login-form-fields-grouped-by-method-1.8.diff]]
  • [get | view] (2011-05-02 21:45:04, 17.4 KB) [[attachment:patch-login-form-fields-grouped-by-method.diff]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.