# HG changeset patch
# User Paul Boddie <paul@boddie.org.uk>
# Date 1299182511 -3600
# Node ID 77bea805221c0b3de5f9af9b49e0bbae0af7e469
# Parent  12a9b828133466eaeff0629a60e870e41b6296e2
Changed the login form to group fields by authentication method, showing each
method's hint below the fields. Each group of fields is within its own HTML
form, so that only relevant fields are submitted.
Added more HTML attributes to the HTML widget classes.

diff -r 12a9b8281334 -r 77bea805221c MoinMoin/userform/login.py
--- a/MoinMoin/userform/login.py	Sat Feb 26 21:28:45 2011 +0100
+++ b/MoinMoin/userform/login.py	Thu Mar 03 21:01:51 2011 +0100
@@ -4,6 +4,7 @@
 
     @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
                 2003-2007 MoinMoin:ThomasWaldmann
+                2011 Paul Boddie <paul@boddie.org.uk>
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -36,54 +37,71 @@
         sn = request.getScriptname()
         pi = request.getPathinfo()
         action = u"%s%s" % (sn, pi)
-        hints = []
+
+        # Modules should really provide label and type details.
+
+        field_name_mapping = {"username" : "name"}
+        field_type_mapping = {"password" : "password"}
+        field_label_mapping = {"username" : _('Name'), "password" : _('Password'), "openid_identifier" : _('OpenID')}
+        field_id_mapping = {"openid_identifier" : "openididentifier"}
+
+        # Generate a collection of login forms, one per module.
+
+        self._forms = html.DIV()
+        self._forms.append(html.P().append(html.Text(_('This site supports the following login methods:'))))
+
         for authm in request.cfg.auth:
-            hint = authm.login_hint(request)
-            if hint:
-                hints.append(hint)
-        self._form = html.FORM(action=action, name="loginform")
-        self._table = html.TABLE(border="0")
 
-        # Use the user interface language and direction
-        lang_attr = request.theme.ui_lang_attr()
-        self._form.append(html.Raw('<div class="userpref"%s>' % lang_attr))
+            # If there are no inputs, don't bother generating a form.
 
-        self._form.append(html.INPUT(type="hidden", name="action", value="login"))
-        self._form.append(self._table)
-        for hint in hints:
-            self._form.append(html.P().append(html.Raw(hint)))
-        self._form.append(html.Raw("</div>"))
+            if not authm.login_inputs:
+                continue
 
-        cfg = request.cfg
-        if 'username' in cfg.auth_login_inputs:
-            self.make_row(_('Name'), [
+            self._form = html.FORM(action=action, name="loginform")
+            self._forms.append(self._form)
+
+            # Use the user interface language and direction.
+
+            lang_attr = request.theme.ui_lang_attr()
+            self._form.append(html.Raw('<div class="userpref"%s>' % lang_attr))
+
+            self._form.append(html.INPUT(type="hidden", name="action", value="login"))
+
+            # Put fields in a table.
+
+            self._table = html.TABLE(border="0", style="table-layout: fixed")
+            self._form.append(self._table)
+
+            # Generate the module's fields.
+
+            for field in authm.login_inputs:
+                name = field_name_mapping.get(field, field)
+                type = field_type_mapping.get(field, "text")
+                label = field_label_mapping.get(field, field)
+                kw = {}
+                if field_id_mapping.has_key(field):
+                    kw["id"] = field_id_mapping[field]
+
+                self.make_row(label, [
+                    html.INPUT(
+                        type=type, size="32", name=name, **kw
+                    ),
+                ], width="20%")
+
+            self.make_row('', [
                 html.INPUT(
-                    type="text", size="32", name="name",
+                    type="submit", name='login', value=_('Login')
                 ),
             ])
 
-        if 'password' in cfg.auth_login_inputs:
-            self.make_row(_('Password'), [
-                html.INPUT(
-                    type="password", size="32", name="password",
-                ),
-            ])
+            # Finish the form with a hint.
 
-        if 'openid_identifier' in cfg.auth_login_inputs:
-            self.make_row(_('OpenID'), [
-                html.INPUT(
-                    type="text", size="32", name="openid_identifier",
-                    id="openididentifier"
-                ),
-            ])
+            hint = authm.login_hint(request)
+            if hint:
+                self._form.append(html.P().append(html.Raw(hint)))
+            self._form.append(html.Raw("</div>"))
 
-        self.make_row('', [
-            html.INPUT(
-                type="submit", name='login', value=_('Login')
-            ),
-        ])
-
-        return unicode(self._form)
+        return unicode(self._forms)
 
 def getLogin(request):
     """ Return HTML code for the login. """
diff -r 12a9b8281334 -r 77bea805221c MoinMoin/widget/html.py
--- a/MoinMoin/widget/html.py	Sat Feb 26 21:28:45 2011 +0100
+++ b/MoinMoin/widget/html.py	Thu Mar 03 21:01:51 2011 +0100
@@ -148,6 +148,7 @@
         'rel': None,
         'rev': None,
         'shape': None,
+        'style': None,
         'tabindex': None,
         'type': None,
     }
@@ -156,18 +157,21 @@
     "abbreviated form (e.g., WWW, HTTP, etc.)"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class ACRONYM(CompositeElement):
     "acronyms"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class ADDRESS(CompositeElement):
     "information on author"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class AREA(EmptyElement):
@@ -177,12 +181,14 @@
         'class': None,
         'href': None,
         'shape': None,
+        'style': None,
     }
 
 class B(CompositeElement):
     "bold text style"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class BASE(EmptyElement):
@@ -194,18 +200,21 @@
     "I18N BiDi over-ride"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class BIG(CompositeElement):
     "large text style"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class BLOCKQUOTE(CompositeElement):
     "long quotation"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class BODY(CompositeElement):
@@ -218,6 +227,7 @@
         'link': None,
         'onload': None,
         'onunload': None,
+        'style': None,
         'text': None,
         'vlink': None,
     }
@@ -226,48 +236,56 @@
     "forced line break"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class BUTTON(CompositeElement):
     "push button"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class CAPTION(CompositeElement):
     "table caption"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class CITE(CompositeElement):
     "citation"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class CODE(CompositeElement):
     "computer code fragment"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class DD(CompositeElement):
     "definition description"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class DEL(CompositeElement):
     "deleted text"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class DFN(CompositeElement):
     "instance definition"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class DIV(CompositeElement):
@@ -275,24 +293,28 @@
     _ATTRS = {
         'id': None,
         'class': None,
+        'style': None,
     }
 
 class DL(CompositeElement):
     "definition list"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class DT(CompositeElement):
     "definition term"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class EM(CompositeElement):
     "emphasis"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class FORM(CompositeElement):
@@ -307,6 +329,7 @@
         'name': None,
         'onreset': None,
         'onsubmit': None,
+        'style': None,
         'target': None,
     }
     _DEFAULT_ATTRS = {
@@ -317,36 +340,42 @@
     "heading"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class H2(CompositeElement):
     "heading"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class H3(CompositeElement):
     "heading"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class H4(CompositeElement):
     "heading"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class H5(CompositeElement):
     "heading"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class H6(CompositeElement):
     "heading"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class HEAD(CompositeElement):
@@ -358,6 +387,7 @@
     "horizontal rule"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class HTML(CompositeElement):
@@ -370,12 +400,14 @@
     "italic text style"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class IFRAME(CompositeElement):
     "inline subwindow"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class IMG(EmptyElement):
@@ -385,6 +417,7 @@
         'alt': None,
         'border': None,
         'class': None,
+        'style': None,
         'vspace': None,
     }
 
@@ -409,6 +442,7 @@
         'readonly': None,
         'size': None,
         'src': None,
+        'style': None,
         'tabindex': None,
         'type': None,
         'usemap': None,
@@ -419,12 +453,14 @@
     "inserted text"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class KBD(CompositeElement):
     "text to be entered by the user"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class LABEL(CompositeElement):
@@ -432,6 +468,7 @@
     _ATTRS = {
         'class': None,
         'for_': None,
+        'style': None,
     }
 
     def _openingtag(self):
@@ -455,6 +492,7 @@
     "list item"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class LINK(EmptyElement):
@@ -467,6 +505,7 @@
         'media': None,
         'rel': None,
         'rev': None,
+        'style': None,
         'target': None,
         'type': None,
     }
@@ -475,6 +514,7 @@
     "client-side image map"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class META(EmptyElement):
@@ -486,18 +526,21 @@
     "alternate content container for non script-based rendering"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class OL(CompositeElement):
     "ordered list"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class OPTGROUP(CompositeElement):
     "option group"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class OPTION(CompositeElement):
@@ -507,6 +550,7 @@
         'disabled': None,
         'label': None,
         'selected': None,
+        'style': None,
         'value': None,
     }
 
@@ -514,24 +558,28 @@
     "paragraph"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class PRE(CompositeElement):
     "preformatted text"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class Q(CompositeElement):
     "short inline quotation"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class SAMP(CompositeElement):
     "sample program output, scripts, etc."
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class SCRIPT(CompositeElement):
@@ -550,6 +598,7 @@
         'onchange': None,
         'onfocus': None,
         'size': None,
+        'style': None,
         'tabindex': None,
     }
 
@@ -557,18 +606,21 @@
     "small text style"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class SPAN(CompositeElement):
     "generic language/style container"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class STRONG(CompositeElement):
     "strong emphasis"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class STYLE(CompositeElement):
@@ -580,12 +632,14 @@
     "subscript"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class SUP(CompositeElement):
     "superscript"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class TABLE(CompositeElement):
@@ -600,6 +654,7 @@
         'frame': None,
         'rules': None,
         'summary': None,
+        'style': None,
         'width': None,
     }
 
@@ -608,6 +663,7 @@
     _ATTRS = {
         'align': None,
         'class': None,
+        'style': None,
     }
 
 class TD(CompositeElement):
@@ -616,7 +672,9 @@
         'abbr': None,
         'align': None,
         'class': None,
+        'style': None,
         'valign': None,
+        'width': None,
         'colspan': None,
         'rowspan': None,
     }
@@ -628,6 +686,7 @@
         'cols': None,
         'name': None,
         'rows': None,
+        'style': None,
     }
 
 class TFOOT(CompositeElement):
@@ -635,6 +694,7 @@
     _ATTRS = {
         'align': None,
         'class': None,
+        'style': None,
     }
 
 class TH(CompositeElement):
@@ -643,6 +703,8 @@
         'abbr': None,
         'align': None,
         'class': None,
+        'style': None,
+        'width': None,
     }
 
 class THEAD(CompositeElement):
@@ -650,6 +712,7 @@
     _ATTRS = {
         'align': None,
         'class': None,
+        'style': None,
     }
 
 class TITLE(CompositeElement):
@@ -662,24 +725,28 @@
     _ATTRS = {
         'align': None,
         'class': None,
+        'style': None,
     }
 
 class TT(CompositeElement):
     "teletype or monospaced text style"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class UL(CompositeElement):
     "unordered list"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 class VAR(CompositeElement):
     "instance of a variable or program argument"
     _ATTRS = {
         'class': None,
+        'style': None,
     }
 
 
# HG changeset patch
# User Paul Boddie <paul@boddie.org.uk>
# Date 1299427097 -3600
# Node ID 6b74207904b3a3d28233051d5945bc1fb9656002
# Parent  1a333e78214f2d26882cee1f2d463e04e6d763df
Made the form identifier unique.

diff -r 1a333e78214f -r 6b74207904b3 MoinMoin/userform/login.py
--- a/MoinMoin/userform/login.py	Sun Mar 06 02:32:17 2011 +0100
+++ b/MoinMoin/userform/login.py	Sun Mar 06 16:58:17 2011 +0100
@@ -50,14 +50,14 @@
         self._forms = html.DIV()
         self._forms.append(html.P().append(html.Text(_('This site supports the following login methods:'))))
 
-        for authm in request.cfg.auth:
+        for formnumber, authm in enumerate(request.cfg.auth):
 
             # If there are no inputs, don't bother generating a form.
 
             if not authm.login_inputs:
                 continue
 
-            self._form = html.FORM(action=action, name="loginform")
+            self._form = html.FORM(action=action, name="loginform%d" % formnumber)
             self._forms.append(self._form)
 
             # Use the user interface language and direction.
