changeset 2351:2b4ae7c40bfa

Merge main.
author Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl>
date Tue, 10 Jul 2007 17:58:14 +0200
parents 61829d040c63 (current diff) 66cc37b8f297 (diff)
children 0b2420a9b14d d9da7dbce6cd
files MoinMoin/action/AttachFile.py MoinMoin/macro/Login.py MoinMoin/packages.py MoinMoin/userform.py
diffstat 21 files changed, 1178 insertions(+), 962 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/pep8.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/_tests/pep8.py	Tue Jul 10 17:58:14 2007 +0200
@@ -507,7 +507,7 @@
 
     def __init__(self, filename):
         self.filename = filename
-        self.lines = file(filename).readlines()
+        self.lines = file(filename, 'rb').readlines()
         self.physical_checks = find_checks('physical_line')
         self.logical_checks = find_checks('logical_line')
         options.counters['physical lines'] = \
--- a/MoinMoin/action/AttachFile.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/action/AttachFile.py	Tue Jul 10 17:58:14 2007 +0200
@@ -892,7 +892,7 @@
         if package.msg != "":
             msg += "<br><pre>" + wikiutil.escape(package.msg) + "</pre>"
     else:
-        msg = _('The file %s is not a MoinMoin package file.' % wikiutil.escape(target))
+        msg = _('The file %s is not a MoinMoin package file.') % wikiutil.escape(target)
 
     upload_form(pagename, request, msg=msg)
 
@@ -975,7 +975,7 @@
                             "files are too big, .zip files only, exist already or "
                             "reside in folders.") % {'filename': filename}
         else:
-            msg = _('The file %(filename)s is not a .zip file.' % {'filename': filename})
+            msg = _('The file %(filename)s is not a .zip file.') % {'filename': filename}
 
     upload_form(pagename, request, msg=wikiutil.escape(msg))
 
--- a/MoinMoin/action/__init__.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/action/__init__.py	Tue Jul 10 17:58:14 2007 +0200
@@ -33,7 +33,7 @@
 modules = pysupport.getPackageModules(__file__)
 
 # builtin-stuff (see do_<name> below):
-names = ['show', 'recall', 'raw', 'format', 'content', 'print', 'refresh', 'goto', 'userform', ]
+names = ['show', 'recall', 'raw', 'format', 'content', 'print', 'refresh', 'goto', ]
 
 class ActionBase:
     """ action base class with some generic stuff to inherit
@@ -279,12 +279,6 @@
     target = request.form.get('target', [''])[0]
     request.http_redirect(Page(request, target).url(request, relative=False))
 
-def do_userform(pagename, request):
-    """ save data posted from UserPreferences """
-    from MoinMoin import userform
-    savemsg = userform.savedata(request)
-    Page(request, pagename).send_page(msg=savemsg)
-
 # Dispatching ----------------------------------------------------------------
 def getNames(cfg):
     if not hasattr(cfg.cache, 'action_names'):
--- a/MoinMoin/action/backup.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/action/backup.py	Tue Jul 10 17:58:14 2007 +0200
@@ -63,11 +63,11 @@
         #files = "<br>".join(files)
         filecount = len(files)
         dircount = len(dirs)
-        return sendMsg(request, pagename, msg=_(
-            'Restored Backup: %(filename)s to target dir: %(targetdir)s.\nFiles: %(filecount)d, Directories: %(dircount)d' %
-                locals()))
+        return sendMsg(request, pagename,
+            msg=_('Restored Backup: %(filename)s to target dir: %(targetdir)s.\nFiles: %(filecount)d, Directories: %(dircount)d') %
+                locals())
     except:
-        return sendMsg(request, pagename, msg=_("Restoring backup: %(filename)s to target dir: %(targetdir)s failed." % locals()))
+        return sendMsg(request, pagename, msg=_("Restoring backup: %(filename)s to target dir: %(targetdir)s failed.") % locals())
 
 def sendBackupForm(request, pagename):
     _ = request.getText
@@ -140,5 +140,4 @@
         sendBackupForm(request, pagename)
     else:
         return sendMsg(request, pagename,
-                       msg=_('Unknown backup subaction: %s.' % dowhat))
-
+                       msg=_('Unknown backup subaction: %s.') % dowhat)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/newaccount.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,116 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - create account action
+
+    @copyright: 2007 MoinMoin:JohannesBerg
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import user, wikiutil, util
+from MoinMoin.Page import Page
+from MoinMoin.widget import html
+import MoinMoin.events as events
+from MoinMoin.userprefs.prefs import Settings
+
+
+_debug = False
+
+def _create_user(request):
+    _ = request.getText
+    form = request.form
+
+    if request.request_method != 'POST':
+        return _("Use UserPreferences to change your settings or create an account.")
+    # Create user profile
+    theuser = user.User(request, auth_method="new-user")
+
+    # Require non-empty name
+    try:
+        theuser.name = form['name'][0]
+    except KeyError:
+        return _("Empty user name. Please enter a user name.")
+
+    # Don't allow creating users with invalid names
+    if not user.isValidName(request, theuser.name):
+        return _("""Invalid user name {{{'%s'}}}.
+Name may contain any Unicode alpha numeric character, with optional one
+space between words. Group page name is not allowed.""") % wikiutil.escape(theuser.name)
+
+    # Name required to be unique. Check if name belong to another user.
+    if user.getUserId(request, theuser.name):
+        return _("This user name already belongs to somebody else.")
+
+    # try to get the password and pw repeat
+    password = form.get('password', [''])[0]
+    password2 = form.get('password2', [''])[0]
+
+    # Check if password is given and matches with password repeat
+    if password != password2:
+        return _("Passwords don't match!")
+    if not password:
+        return _("Please specify a password!")
+
+    # Encode password
+    if password and not password.startswith('{SHA}'):
+        try:
+            theuser.enc_password = user.encodePassword(password)
+        except UnicodeError, err:
+            # Should never happen
+            return "Can't encode password: %s" % str(err)
+
+    # try to get the email, for new users it is required
+    email = wikiutil.clean_input(form.get('email', [''])[0])
+    theuser.email = email.strip()
+    if not theuser.email:
+        return _("Please provide your email address. If you lose your"
+                 " login information, you can get it by email.")
+
+    # Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
+    if theuser.email and request.cfg.user_email_unique:
+        users = user.getUserList(request)
+        for uid in users:
+            if uid == theuser.id:
+                continue
+            thisuser = user.User(request, uid)
+            if thisuser.email == theuser.email and not thisuser.disabled:
+                return _("This email already belongs to somebody else.")
+
+    # save data
+    theuser.save()
+
+    user_created = events.UserCreatedEvent(request, theuser)
+    events.send_event(user_created)
+
+    if form.has_key('create_and_mail'):
+        theuser.mailAccountData()
+
+    result = _("User account created! You can use this account to login now...")
+    if _debug:
+        result = result + util.dumpFormData(form)
+    return result
+
+
+def execute(pagename, request):
+    pagename = pagename
+    page = Page(request, pagename)
+    _ = request.getText
+    form = request.form
+
+    submitted = form.has_key('create_only') or form.has_key('create_and_mail')
+
+    if submitted: # user pressed create button
+        error = _create_user(request)
+        return page.send_page(msg=error)
+    else: # show create form
+        request.emit_http_headers()
+        request.theme.send_title(_("Create Account"), pagename=pagename)
+
+        request.write(request.formatter.startContent("content"))
+
+        # THIS IS A BIG HACK. IT NEEDS TO BE CLEANED UP
+        request.write(Settings(request).create_form(create_only=True))
+
+        request.write(request.formatter.endContent())
+
+        request.theme.send_footer(pagename)
+        request.theme.send_closing_html()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/recoverpass.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,68 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - create account action
+
+    @copyright: 2007 MoinMoin:JohannesBerg
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import user, wikiutil
+from MoinMoin.Page import Page
+from MoinMoin.widget import html
+from MoinMoin.userprefs.prefs import Settings
+
+def _do_recover(request):
+    _ = request.getText
+    form = request.form
+    if not request.cfg.mail_enabled:
+        return _("""This wiki is not enabled for mail processing.
+Contact the owner of the wiki, who can enable email.""")
+    try:
+        email = wikiutil.clean_input(form['email'][0].lower())
+    except KeyError:
+        return _("Please provide a valid email address!")
+
+    u = user.get_by_email_address(request, email)
+    if u:
+        msg = u.mailAccountData()
+        return wikiutil.escape(msg)
+
+    return _("Found no account matching the given email address '%(email)s'!") % {'email': email}
+
+
+def execute(pagename, request):
+    pagename = pagename
+    page = Page(request, pagename)
+    _ = request.getText
+    form = request.form
+
+    submitted = form.get('account_sendmail', [''])[0]
+
+    if submitted: # user pressed create button
+        msg = _do_recover(request)
+        page.send_page(msg=msg)
+    else: # show create form
+        request.emit_http_headers()
+        request.theme.send_title(_("Lost password"), pagename=pagename)
+
+        request.write(request.formatter.startContent("content"))
+
+        if not request.cfg.mail_enabled:
+            request.write(_("""This wiki is not enabled for mail processing.
+Contact the owner of the wiki, who can enable email."""))
+        else:
+            # THIS IS A BIG HACK. IT NEEDS TO BE CLEANED UP
+            request.write(Settings(request).create_form(recover_only=True))
+
+            request.write(_("""
+== Recovering a lost password ==
+[[BR]]
+If you have forgotten your password, provide your email address and click on '''Mail me my account data'''.
+[[BR]]
+The email you get contains the encrypted password (so even if someone intercepts the mail, he won't know your REAL password). Just copy and paste it into the login mask into the password field and log in.
+After logging in you should change your password."""))
+
+        request.write(request.formatter.endContent())
+
+        request.theme.send_footer(pagename)
+        request.theme.send_closing_html()
--- a/MoinMoin/action/userprefs.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/action/userprefs.py	Tue Jul 10 17:58:14 2007 +0200
@@ -3,21 +3,84 @@
     MoinMoin - UserPreferences action
 
     This is a simple plugin, that adds a "UserPreferences" action.
-    This action will display the UserPreferences page (or appropriate
-    page in the reader's language), so that the user can login, or
-    change his/her preferences.
-
-    However, as it is an action, the page that is displayed is not
-    changed. After submitting the form, the user is presented the
-    same page he/she was seeing before, and the trail is not modified.
 
     @copyright: 2006 Radomir Dopieralski
+                2007 MoinMoin:JohannesBerg
     @license: GNU GPL, see COPYING for details.
 """
 
 from MoinMoin import wikiutil
+from MoinMoin.Page import Page
+from MoinMoin.widget import html
+
+def _handle_submission(request):
+    """ Handle GET and POST requests of preferences forms.
+
+    Return error msg or None.
+    """
+    _ = request.getText
+    sub = request.form.get('handler', [None])[0]
+    try:
+        cls = wikiutil.importPlugin(request.cfg, 'userprefs', sub, 'Settings')
+    except wikiutil.PluginMissingError:
+        return _("No such preferences plugin")
+
+    obj = cls(request)
+    if not obj.allowed():
+        # intentionally do not let the user know this exists
+        return _("No such preferences plugin")
+    return obj.handle_form()
+
+def _create_prefs_page(request, sel=None):
+    _ = request.getText
+    plugins = wikiutil.getPlugins('userprefs', request.cfg)
+    ret = html.P()
+    ret.append(html.Text(_("Please choose:")))
+    ret.append(html.BR())
+    items = html.UL()
+    ret.append(items)
+    for sub in plugins:
+        cls = wikiutil.importPlugin(request.cfg, 'userprefs', sub, 'Settings')
+        obj = cls(request)
+        if not obj.allowed():
+            continue
+        url = request.page.url(request, {'action': 'userprefs', 'sub': sub})
+        lnk = html.LI().append(html.A(href=url).append(html.Text(obj.title)))
+        items.append(lnk)
+    return unicode(ret)
+
+
+def _create_page(request, cancel=False):
+    # returns text, title, msg
+    pagename = request.page.page_name
+
+    if 'handler' in request.form:
+        return _create_prefs_page(request), None, _handle_submission(request)
+
+    sub = request.form.get('sub', [''])[0]
+    try:
+        cls = wikiutil.importPlugin(request.cfg, 'userprefs', sub, 'Settings')
+    except wikiutil.PluginMissingError:
+        return _create_prefs_page(request), None, None
+
+    obj = cls(request)
+    return obj.create_form(), obj.title, None
+
 
 def execute(pagename, request):
-    page = wikiutil.getLocalizedPage(request, 'UserPreferences')
-    page.send_page()
-
+    _ = request.getText
+    text, title, msg = _create_page(request)
+    if not title:
+        title = _("Settings", formatted=False)
+    else:
+        lnk = html.A(href='xx').append(html.Text(_("Settings", formatted=False)))
+        lnk = unicode(lnk)
+        title = _("Settings") + "/" + title
+    request.emit_http_headers()
+    request.theme.send_title(title, page=request.page, pagename=pagename, msg=msg)
+    # Start content (important for RTL support)
+    request.write(request.formatter.startContent("content"))
+    request.write(text)
+    request.write(request.formatter.endContent())
+    request.theme.send_footer(pagename)
+    request.theme.send_closing_html()
--- a/MoinMoin/macro/EmbedObject.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/macro/EmbedObject.py	Tue Jul 10 17:58:14 2007 +0200
@@ -170,14 +170,14 @@
         _ = self._
 
         if not mt:
-            return _("Not supported mimetype of file: %s" % self.target)
+            return _("Not supported mimetype of file: %s") % self.target
 
         mime_type = "%s/%s" % (mt.major, mt.minor, )
         dangerous = mime_type in self.request.cfg.mimetypes_xss_protect
 
         if not mime_type in self.request.cfg.mimetypes_embed or dangerous:
             kw = {'src': url}
-            return "%s: %s%s%s" % (self.macro.formatter.text('Embedding of object by choosen formatter not possible'),
+            return "%s: %s%s%s" % (self.macro.formatter.text(_('Embedding of object by choosen formatter not possible')),
                                self.macro.formatter.url(1, kw['src']),
                                self.macro.formatter.text(self.target),
                                self.macro.formatter.url(0))
@@ -266,7 +266,7 @@
         _ = self._
 
         if not self.target:
-            msg = 'Not enough arguments to EmbedObject macro! Try [[EmbedObject(attachment [,width=width] [,height=height] [,alt=Embedded mimetpye/xy])]]'
+            msg = _('Not enough arguments to EmbedObject macro! Try [[EmbedObject(attachment [,width=width] [,height=height] [,alt=Embedded mimetpye/xy])]]', formatted=False)
             return "%s%s%s" % (self.formatter.sysmsg(1), self.formatter.text(msg), self.formatter.sysmsg(0))
 
         pagename, fname = AttachFile.absoluteName(self.target, self.formatter.page.page_name)
--- a/MoinMoin/macro/Login.py	Tue Jul 10 17:57:58 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - login and sendmail form
-
-    @copyright: 2005-2006 Radomirs Cirskis <nad2000@gmail.com>,
-                2007 MoinMoin:Reimar Bauer, Oliver Siemoneit
-    @license: GNU GPL, see COPYING for details.
-"""
-from MoinMoin.widget import html
-from MoinMoin import userform
-
-def make_row(table, label, cell, **kw):
-    """ Create a row in the form table.
-    """
-    table.append(html.TR().extend([
-        html.TD(**kw).extend([html.B().append(label), '   ']),
-        html.TD().extend(cell),
-    ]))
-    return table
-
-def execute(macro, args):
-    """ Show the login form (but only when not logged in) """
-    request = macro.request
-    _ = request.getText
-    if not args:
-        if request.user.valid:
-            data = u''
-        else:
-            data = userform.getLogin(request)
-        return data
-
-    elif args == "sendmail":
-        sn = request.getScriptname()
-        pi = request.getPathinfo()
-        action = u"%s%s" % (sn, pi)
-        form = html.FORM(action=action)
-        table = html.TABLE()
-
-        if not request.cfg.mail_enabled:
-            return _("This wiki is not enabled for mail processing.\nContact the owner of the wiki, who can enable email.")
-        else:
-            buttons = []
-            action = u"%s%s" % (sn, pi)
-            form = html.FORM(action=action)
-            table = html.TABLE(border="0")
-
-            # Add form fields
-            for key, label, type, length, textafter in request.cfg.user_form_fields:
-                if key == 'email':
-                    table = make_row(table, _(label),
-                                     [html.INPUT(type=type, size=length, name=key,
-                                     value=''), ' ', ])
-            # Add buttons
-            buttons.append(("account_sendmail", _('Mail me my account data')))
-            button_cell = []
-            for name, label in buttons:
-                if not name in request.cfg.user_form_remove:
-                    button_cell.extend([
-                        html.INPUT(type="submit", name=name, value=label),
-                        ' ',
-                    ])
-            make_row(table, '', button_cell)
-
-            # Use the user interface language and direction
-            lang_attr = request.theme.ui_lang_attr()
-            form.append(html.Raw('<div class="userpref"%s>' % lang_attr))
-            form.append(html.INPUT(type="hidden", name="action", value="userform"))
-            form.append(table)
-            form.append(html.Raw("</div>"))
-
-            return unicode(form)
-    else:
-        return _("Check your argument %s" % (args))
--- a/MoinMoin/macro/__init__.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/macro/__init__.py	Tue Jul 10 17:58:14 2007 +0200
@@ -25,7 +25,7 @@
 from MoinMoin.Page import Page
 
 names = ["TitleSearch", "WordIndex", "TitleIndex",
-         "GoTo", "InterWiki", "PageCount", "UserPreferences",
+         "GoTo", "InterWiki", "PageCount",
          # Macros with arguments
          "Icon", "PageList", "Date", "DateTime", "Anchor", "MailTo", "GetVal",
          "TemplateList",
@@ -71,7 +71,6 @@
         "PageList": ["namespace"],
         "Date": ["time"],
         "DateTime": ["time"],
-        "UserPreferences": ["time"],
         "Anchor": [],
         "Mailto": ["user"],
         "GetVal": ["pages"],
@@ -387,19 +386,6 @@
     def _macro_DateTime(self, args):
         return self.__get_Date(args, self.request.user.getFormattedDateTime)
 
-
-    def _macro_UserPreferences(self, args):
-        from MoinMoin import userform
-
-        create_only = False
-        if isinstance(args, unicode):
-            args = args.strip(" '\"")
-            create_only = (args.lower() == "createonly")
-
-        return self.formatter.rawHTML(userform.getUserForm(
-            self.request,
-            create_only=create_only))
-
     def _macro_Anchor(self, args):
         return self.formatter.anchordef(args or "anchor")
 
--- a/MoinMoin/packages.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/packages.py	Tue Jul 10 17:58:14 2007 +0200
@@ -279,7 +279,7 @@
                 raise RuntimeScriptException(_("Installation of '%(filename)s' failed.") % {
                     'filename': filename} + "\n" + package.msg)
         else:
-            raise RuntimeScriptException(_('The file %s is not a MoinMoin package file.' % filename))
+            raise RuntimeScriptException(_('The file %s is not a MoinMoin package file.') % filename)
 
         self.msg += package.msg
 
--- a/MoinMoin/parser/_tests/test_text_moin_wiki.py	Tue Jul 10 17:57:58 2007 +0200
+++ b/MoinMoin/parser/_tests/test_text_moin_wiki.py	Tue Jul 10 17:58:14 2007 +0200
@@ -161,22 +161,30 @@
 class TestDateTimeMacro(ParserTestCase):
     """ Test DateTime macro
 
-    Might fail due to libc problems, therefore the fail message warn
-    about this.
+    If you get failures in these tests, it might be because:
+    * libc problems (some are just broken/incorrect)
+    * changes in the timezone of a country (e.g. Lithuania seems
+      to have changed the tz it is in, see comments below). Our
+      timestamps are in UTC, but we use mktime(), which is the inverse
+      function of localtime() (NOT of gmtime()), so we have to fix
+      our calculation with the tzoffset. Problem: we can't easily find
+      out the tzoffset some location had at some time in the past.
+      Badly enough, we also don't have an inverse function of gmtime().
 
-    TODO: when this test fail, does it mean that moin code fail on that
-    machine? - can we fix this?
+    If some of these tests fail and show differences of e.g. 1 hour,
+    you might see your timestamps being off by 1 hour in the wiki.
+    If you can live with that, this will cause no other problems.
     """
 
     text = 'AAA %s AAA'
     needle = re.compile(text % r'(.+)')
     _tests = (
         # test                                   expected
-        ('[[DateTime(1970-01-06T00:00:00)]]',   '1970-01-06 00:00:00'),
         ('[[DateTime(259200)]]',                '1970-01-04 00:00:00'),
         ('[[DateTime(2003-03-03T03:03:03)]]',   '2003-03-03 03:03:03'),
-        ('[[DateTime(2000-01-01T00:00:00Z)]]',  '2000-01-01 00:00:00'),
+        ('[[DateTime(2000-01-01T00:00:00Z)]]',  '2000-01-01 00:00:00'), # works for Europe/Vilnius
         ('[[Date(2002-02-02T01:02:03Z)]]',      '2002-02-02'),
+        ('[[DateTime(1970-01-06T00:00:00)]]',   '1970-01-06 00:00:00'), # fails e.g. for Europe/Vilnius
         )
 
     def setUp(self):
@@ -191,9 +199,10 @@
         note = """
 
     If this fails, it is likely a problem in your python / libc,
-    not in moin.  See also:
-    <http://sourceforge.net/tracker/index.php?func=detail&
-       aid=902172&group_id=5470&atid=105470>"""
+    not in moin.  See also: <http://sourceforge.net/tracker/index.php?func=detail&aid=902172&group_id=5470&atid=105470>
+    
+    It can also be related to TZ changes a country historically made.
+    """
 
         for test, expected in self._tests:
             html = self.parse(self.text % test)
--- a/MoinMoin/userform.py	Tue Jul 10 17:57:58 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,834 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - UserPreferences Form and User Browser
-
-    @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
-                2003-2007 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import time
-from MoinMoin import user, util, wikiutil
-import MoinMoin.events as events
-from MoinMoin.widget import html
-
-_debug = 0
-
-#############################################################################
-### Form POST Handling
-#############################################################################
-
-def savedata(request):
-    """ Handle POST request of the user preferences form.
-
-    Return error msg or None.
-    """
-    return UserSettingsHandler(request).handle_form()
-
-
-class UserSettingsHandler:
-
-    def __init__(self, request):
-        """ Initialize user settings form. """
-        self.request = request
-        self._ = request.getText
-        self.cfg = request.cfg
-
-    def _decode_pagelist(self, key):
-        """ Decode list of pages from form input
-
-        Each line is a page name, empty lines ignored.
-
-        @param key: the form key to get
-        @rtype: list of unicode strings
-        @return: list of normalized names
-        """
-        text = self.request.form.get(key, [''])[0]
-        text = text.replace('\r', '')
-        items = []
-        for item in text.split('\n'):
-            item = item.strip()
-            if not item:
-                continue
-            items.append(item)
-        return items
-
-    def _account_sendmail(self):
-        _ = self._
-        form = self.request.form
-
-        if not self.cfg.mail_enabled:
-            return _("""This wiki is not enabled for mail processing.
-Contact the owner of the wiki, who can enable email.""")
-        try:
-            email = wikiutil.clean_input(form['email'][0].lower())
-        except KeyError:
-            return _("Please provide a valid email address!")
-
-        u = user.get_by_email_address(self.request, email)
-        if u:
-            msg = u.mailAccountData()
-            return wikiutil.escape(msg)
-
-        return _("Found no account matching the given email address '%(email)s'!") % {'email': email}
-
-    def _create_user(self):
-        _ = self._
-        form = self.request.form
-
-        if self.request.request_method != 'POST':
-            return _("Use UserPreferences to change your settings or create an account.")
-        # Create user profile
-        theuser = user.User(self.request, auth_method="new-user")
-
-        # Require non-empty name
-        try:
-            theuser.name = form['name'][0]
-        except KeyError:
-            return _("Empty user name. Please enter a user name.")
-
-        # Don't allow creating users with invalid names
-        if not user.isValidName(self.request, theuser.name):
-            return _("""Invalid user name {{{'%s'}}}.
-Name may contain any Unicode alpha numeric character, with optional one
-space between words. Group page name is not allowed.""") % wikiutil.escape(theuser.name)
-
-        # Name required to be unique. Check if name belong to another user.
-        if user.getUserId(self.request, theuser.name):
-            return _("This user name already belongs to somebody else.")
-
-        # try to get the password and pw repeat
-        password = form.get('password', [''])[0]
-        password2 = form.get('password2', [''])[0]
-
-        # Check if password is given and matches with password repeat
-        if password != password2:
-            return _("Passwords don't match!")
-        if not password:
-            return _("Please specify a password!")
-
-        # Encode password
-        if password and not password.startswith('{SHA}'):
-            try:
-                theuser.enc_password = user.encodePassword(password)
-            except UnicodeError, err:
-                # Should never happen
-                return "Can't encode password: %s" % str(err)
-
-        # try to get the email, for new users it is required
-        email = wikiutil.clean_input(form.get('email', [''])[0])
-        theuser.email = email.strip()
-        if not theuser.email:
-            return _("Please provide your email address. If you lose your"
-                     " login information, you can get it by email.")
-
-        # Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
-        if theuser.email and self.request.cfg.user_email_unique:
-            users = user.getUserList(self.request)
-            for uid in users:
-                if uid == theuser.id:
-                    continue
-                thisuser = user.User(self.request, uid)
-                if thisuser.email == theuser.email and not thisuser.disabled:
-                    return _("This email already belongs to somebody else.")
-
-        # save data
-        theuser.save()
-
-        user_created = events.UserCreatedEvent(self.request, theuser)
-        events.send_event(user_created)
-
-        if form.has_key('create_and_mail'):
-            theuser.mailAccountData()
-
-        result = _("User account created! You can use this account to login now...")
-        if _debug:
-            result = result + util.dumpFormData(form)
-        return result
-
-    def _select_user(self):
-        _ = self._
-        form = self.request.form
-
-        if (wikiutil.checkTicket(self.request, self.request.form['ticket'][0]) and
-            self.request.request_method == 'POST' and
-            (self.request.user.isSuperUser() or
-             (not self.request._setuid_real_user is None
-              and (self.request._setuid_real_user.isSuperUser())))):
-            su_user = form.get('selected_user', [''])[0]
-            uid = user.getUserId(self.request, su_user)
-            if (not self.request._setuid_real_user is None
-                and uid == self.request._setuid_real_user.id):
-                del self.request.session['setuid']
-                self.request.user = self.request._setuid_real_user
-                self.request._setuid_real_user = None
-            else:
-                theuser = user.User(self.request, uid, auth_method='setuid')
-                theuser.disabled = None
-                self.request.session['setuid'] = uid
-                self.request._setuid_real_user = self.request.user
-                # now continue as the other user
-                self.request.user = theuser
-            return  _("Use UserPreferences to change settings of the selected user account, log out to get back to your account.")
-        else:
-            return _("Use UserPreferences to change your settings or create an account.")
-
-    def _save_user_prefs(self):
-        _ = self._
-        form = self.request.form
-
-        if self.request.request_method != 'POST':
-            return _("Use UserPreferences to change your settings or create an account.")
-        theuser = self.request.user
-        if not theuser:
-            return
-
-        if not 'name' in theuser.auth_attribs:
-            # Require non-empty name
-            theuser.name = form.get('name', [theuser.name])[0]
-
-            # Don't allow changing the name to an invalid one
-            if not user.isValidName(self.request, theuser.name):
-                return _("""Invalid user name {{{'%s'}}}.
-Name may contain any Unicode alpha numeric character, with optional one
-space between words. Group page name is not allowed.""") % wikiutil.escape(theuser.name)
-
-            # Is this an existing user trying to change information or a new user?
-            # Name required to be unique. Check if name belong to another user.
-            if user.getUserId(self.request, theuser.name):
-                if theuser.name != self.request.user.name:
-                    return _("This user name already belongs to somebody else.")
-
-            if not theuser.name:
-                return _("Empty user name. Please enter a user name.")
-
-        if not 'password' in theuser.auth_attribs:
-            # try to get the password and pw repeat
-            password = form.get('password', [''])[0]
-            password2 = form.get('password2', [''])[0]
-
-            # Check if password is given and matches with password repeat
-            if password != password2:
-                return _("Passwords don't match!")
-
-            # Encode password
-            if password and not password.startswith('{SHA}'):
-                try:
-                    theuser.enc_password = user.encodePassword(password)
-                except UnicodeError, err:
-                    # Should never happen
-                    return "Can't encode password: %s" % str(err)
-
-        if not 'email' in theuser.auth_attribs:
-            # try to get the email
-            email = wikiutil.clean_input(form.get('email', [theuser.email])[0])
-            theuser.email = email.strip()
-
-            # Require email
-            if not theuser.email:
-                return _("Please provide your email address. If you lose your"
-                         " login information, you can get it by email.")
-
-            # Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
-            if theuser.email and self.request.cfg.user_email_unique:
-                other = user.get_by_email_address(self.request, theuser.email)
-                if other is not None and other.id != theuser.id:
-                    return _("This email already belongs to somebody else.")
-
-        if not 'jid' in theuser.auth_attribs:
-            # try to get the jid
-            jid = wikiutil.clean_input(form.get('jid', "")[0]).strip()
-
-            jid_changed = theuser.jid != jid
-            previous_jid = theuser.jid
-            theuser.jid = jid
-
-            if theuser.jid and self.request.cfg.user_jid_unique:
-                other = user.get_by_jabber_id(self.request, theuser.jid)
-                if other is not None and other.id != theuser.id:
-                    return _("This jabber id already belongs to somebody else.")
-
-            if jid_changed:
-                set_event = events.JabberIDSetEvent(self.request, theuser.jid)
-                unset_event = events.JabberIDUnsetEvent(self.request, previous_jid)
-                events.send_event(unset_event)
-                events.send_event(set_event)
-
-        if not 'aliasname' in theuser.auth_attribs:
-            # aliasname
-            theuser.aliasname = wikiutil.clean_input(form.get('aliasname', [''])[0])
-
-        # editor size
-        theuser.edit_rows = util.web.getIntegerInput(self.request, 'edit_rows', theuser.edit_rows, 10, 60)
-
-        # try to get the editor
-        theuser.editor_default = form.get('editor_default', [self.cfg.editor_default])[0]
-        theuser.editor_ui = form.get('editor_ui', [self.cfg.editor_ui])[0]
-
-        # time zone
-        theuser.tz_offset = util.web.getIntegerInput(self.request, 'tz_offset', theuser.tz_offset, -84600, 84600)
-
-        # datetime format
-        try:
-            dt_d_combined = UserSettings._date_formats.get(form['datetime_fmt'][0], '')
-            theuser.datetime_fmt, theuser.date_fmt = dt_d_combined.split(' & ')
-        except (KeyError, ValueError):
-            theuser.datetime_fmt = '' # default
-            theuser.date_fmt = '' # default
-
-        # try to get the (optional) theme
-        theme_name = form.get('theme_name', [self.cfg.theme_default])[0]
-        if theme_name != theuser.theme_name:
-            # if the theme has changed, load the new theme
-            # so the user has a direct feedback
-            # WARNING: this should be refactored (i.e. theme load
-            # after userform handling), cause currently the
-            # already loaded theme is just replaced (works cause
-            # nothing has been emitted yet)
-            theuser.theme_name = theme_name
-            if self.request.loadTheme(theuser.theme_name) > 0:
-                theme_name = wikiutil.escape(theme_name)
-                return _("The theme '%(theme_name)s' could not be loaded!") % locals()
-
-        # try to get the (optional) preferred language
-        theuser.language = form.get('language', [''])[0]
-
-        # I want to handle all inputs from user_form_fields, but
-        # don't want to handle the cases that have already been coded
-        # above.
-        # This is a horribly fragile kludge that's begging to break.
-        # Something that might work better would be to define a
-        # handler for each form field, instead of stuffing them all in
-        # one long and inextensible method.  That would allow for
-        # plugins to provide methods to validate their fields as well.
-        already_handled = ['name', 'password', 'password2', 'email',
-                           'aliasname', 'edit_rows', 'editor_default',
-                           'editor_ui', 'tz_offset', 'datetime_fmt',
-                           'theme_name', 'language', 'jid']
-        for field in self.cfg.user_form_fields:
-            key = field[0]
-            if ((key in self.cfg.user_form_disable)
-                or (key in already_handled)):
-                continue
-            default = self.cfg.user_form_defaults[key]
-            value = form.get(key, [default])[0]
-            setattr(theuser, key, value)
-
-        # checkbox options
-        for key, label in self.cfg.user_checkbox_fields:
-            if key not in self.cfg.user_checkbox_disable and key not in self.cfg.user_checkbox_remove:
-                value = form.get(key, ["0"])[0]
-                try:
-                    value = int(value)
-                except ValueError:
-                    pass
-                else:
-                    setattr(theuser, key, value)
-
-        # quicklinks for navibar
-        theuser.quicklinks = self._decode_pagelist('quicklinks')
-
-        # subscription for page change notification
-        theuser.subscribed_pages = self._decode_pagelist('subscribed_pages')
-
-        # subscription to various events
-        available = self.request.cfg.subscribable_events
-        theuser.subscribed_events = [ev for ev in form.get('events', [])]
-
-        # save data
-        theuser.save()
-        self.request.user = theuser
-
-        result = _("User preferences saved!")
-        if _debug:
-            result = result + util.dumpFormData(form)
-        return result
-
-
-    def handle_form(self):
-        _ = self._
-        form = self.request.form
-
-        if form.has_key('cancel'):
-            return
-
-        if form.has_key('account_sendmail'):
-            return self._account_sendmail()
-
-        if (form.has_key('create') or
-            form.has_key('create_only') or
-            form.has_key('create_and_mail')):
-            return self._create_user()
-
-
-        # Select user profile (su user)
-        if form.has_key('select_user'):
-            return self._select_user()
-
-        if form.has_key('save'): # Save user profile
-            return self._save_user_prefs()
-
-
-#############################################################################
-### Form Generation
-#############################################################################
-
-class UserSettings:
-    """ User login and settings management. """
-
-    _date_formats = { # datetime_fmt & date_fmt
-        'iso': '%Y-%m-%d %H:%M:%S & %Y-%m-%d',
-        'us': '%m/%d/%Y %I:%M:%S %p & %m/%d/%Y',
-        'euro': '%d.%m.%Y %H:%M:%S & %d.%m.%Y',
-        'rfc': '%a %b %d %H:%M:%S %Y & %a %b %d %Y',
-    }
-
-    def __init__(self, request):
-        """ Initialize user settings form.
-        """
-        self.request = request
-        self._ = request.getText
-        self.cfg = request.cfg
-
-    def _tz_select(self):
-        """ Create time zone selection. """
-        tz = 0
-        if self.request.user.valid:
-            tz = int(self.request.user.tz_offset)
-
-        options = []
-        now = time.time()
-        for halfhour in range(-47, 48):
-            offset = halfhour * 1800
-            t = now + offset
-
-            options.append((
-                str(offset),
-                '%s [%s%s:%s]' % (
-                    time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple(t)),
-                    "+-"[offset < 0],
-                    "%02d" % (abs(offset) / 3600),
-                    "%02d" % (abs(offset) % 3600 / 60),
-                ),
-            ))
-
-        return util.web.makeSelection('tz_offset', options, str(tz))
-
-
-    def _dtfmt_select(self):
-        """ Create date format selection. """
-        _ = self._
-        try:
-            dt_d_combined = '%s & %s' % (self.request.user.datetime_fmt, self.request.user.date_fmt)
-            selected = [
-                k for k, v in self._date_formats.items()
-                    if v == dt_d_combined][0]
-        except IndexError:
-            selected = ''
-        options = [('', _('Default'))] + self._date_formats.items()
-
-        return util.web.makeSelection('datetime_fmt', options, selected)
-
-
-    def _lang_select(self):
-        """ Create language selection. """
-        from MoinMoin import i18n
-        _ = self._
-        cur_lang = self.request.user.valid and self.request.user.language or ''
-        langs = i18n.wikiLanguages().items()
-        langs.sort(lambda x, y: cmp(x[1]['x-language'], y[1]['x-language']))
-        options = [('', _('<Browser setting>', formatted=False))]
-        for lang in langs:
-            name = lang[1]['x-language']
-            options.append((lang[0], name))
-
-        return util.web.makeSelection('language', options, cur_lang)
-
-    def _user_select(self):
-        options = []
-        users = user.getUserList(self.request)
-        realuid = None
-        if hasattr(self.request, '_setuid_real_user') and self.request._setuid_real_user:
-            realuid = self.request._setuid_real_user.id
-        else:
-            realuid = self.request.user.id
-        for uid in users:
-            if uid != realuid:
-                name = user.User(self.request, id=uid).name # + '/' + uid # for debugging
-                options.append((name, name))
-        options.sort()
-
-        size = min(5, len(options))
-        current_user = self.request.user.name
-        return util.web.makeSelection('selected_user', options, current_user, size=size)
-
-    def _theme_select(self):
-        """ Create theme selection. """
-        cur_theme = self.request.user.valid and self.request.user.theme_name or self.cfg.theme_default
-        options = [("<default>", "<%s>" % self._("Default"))]
-        for theme in wikiutil.getPlugins('theme', self.request.cfg):
-            options.append((theme, theme))
-
-        return util.web.makeSelection('theme_name', options, cur_theme)
-
-    def _event_select(self):
-        """ Create event subscription list. """
-
-        event_list = self.request.cfg.subscribable_events
-        selected = self.request.user.subscribed_events
-        super = self.request.user.isSuperUser()
-
-        # Create a list of (value, name) tuples for display in <select>
-        # Only include super-user visible events if current user has these rights.
-        # It's cosmetic - the check for super-user rights should be performed
-        # in event handling code as well!
-        allowed = []
-        for key in event_list.keys():
-            if not event_list[key]['superuser'] or super:
-                allowed.append((key, event_list[key]['desc']))
-
-        return util.web.makeMultiSelection('events', allowed, selectedvals=selected)
-
-    def _editor_default_select(self):
-        """ Create editor selection. """
-        editor_default = self.request.user.valid and self.request.user.editor_default or self.cfg.editor_default
-        options = [("<default>", "<%s>" % self._("Default"))]
-        for editor in ['text', 'gui', ]:
-            options.append((editor, editor))
-        return util.web.makeSelection('editor_default', options, editor_default)
-
-    def _editor_ui_select(self):
-        """ Create editor selection. """
-        editor_ui = self.request.user.valid and self.request.user.editor_ui or self.cfg.editor_ui
-        options = [("<default>", "<%s>" % self._("Default")),
-                   ("theonepreferred", self._("the one preferred")),
-                   ("freechoice", self._("free choice")),
-                  ]
-        return util.web.makeSelection('editor_ui', options, editor_ui)
-
-    def make_form(self):
-        """ Create the FORM, and the TABLE with the input fields
-        """
-        sn = self.request.getScriptname()
-        pi = self.request.getPathinfo()
-        action = u"%s%s" % (sn, pi)
-        self._form = html.FORM(action=action)
-        self._table = html.TABLE(border="0")
-
-        # Use the user interface language and direction
-        lang_attr = self.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="userform"))
-        self._form.append(self._table)
-        self._form.append(html.Raw("</div>"))
-
-
-    def make_row(self, label, cell, **kw):
-        """ Create a row in the form table.
-        """
-        self._table.append(html.TR().extend([
-            html.TD(**kw).extend([html.B().append(label), '   ']),
-            html.TD().extend(cell),
-        ]))
-
-
-    def asHTML(self, create_only=False):
-        """ Create the complete HTML form code. """
-        _ = self._
-        self.make_form()
-        superuserform = u''
-
-        if (self.request.user.isSuperUser() or
-            (not self.request._setuid_real_user is None and
-             self.request._setuid_real_user.isSuperUser())):
-            ticket = wikiutil.createTicket(self.request)
-            self.make_row(_('Select User'), [self._user_select()])
-            self._form.append(html.INPUT(type="hidden", name="ticket", value="%s" % ticket))
-            buttons = [("select_user", _('Select User'))]
-            button_cell = []
-            for name, label in buttons:
-                button_cell.extend([
-                    html.INPUT(type="submit", name=name, value=label),
-                    ' ',
-                ])
-            self.make_row('', button_cell)
-            superuserform = unicode(self._form)
-            self.make_form()
-
-        if self.request.user.valid and not create_only:
-            buttons = [('save', _('Save')), ('cancel', _('Cancel')), ]
-            uf_remove = self.cfg.user_form_remove
-            uf_disable = self.cfg.user_form_disable
-            for attr in self.request.user.auth_attribs:
-                if attr == 'password':
-                    uf_remove.append(attr)
-                    uf_remove.append('password2')
-                else:
-                    uf_disable.append(attr)
-            for key, label, type, length, textafter in self.cfg.user_form_fields:
-                default = self.cfg.user_form_defaults[key]
-                if not key in uf_remove:
-                    if key in uf_disable:
-                        self.make_row(_(label),
-                                  [html.INPUT(type=type, size=length, name=key, disabled="disabled",
-                                   value=getattr(self.request.user, key)), ' ', _(textafter), ])
-                    else:
-                        self.make_row(_(label),
-                                  [html.INPUT(type=type, size=length, name=key, value=getattr(self.request.user, key)), ' ', _(textafter), ])
-
-            if not self.cfg.theme_force and not "theme_name" in self.cfg.user_form_remove:
-                self.make_row(_('Preferred theme'), [self._theme_select()])
-
-            if not self.cfg.editor_force:
-                if not "editor_default" in self.cfg.user_form_remove:
-                    self.make_row(_('Editor Preference'), [self._editor_default_select()])
-                if not "editor_ui" in self.cfg.user_form_remove:
-                    self.make_row(_('Editor shown on UI'), [self._editor_ui_select()])
-
-            if not "tz_offset" in self.cfg.user_form_remove:
-                self.make_row(_('Time zone'), [
-                    _('Your time is'), ' ',
-                    self._tz_select(),
-                    html.BR(),
-                    _('Server time is'), ' ',
-                    time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple()),
-                    ' (UTC)',
-                ])
-
-            if not "datetime_fmt" in self.cfg.user_form_remove:
-                self.make_row(_('Date format'), [self._dtfmt_select()])
-
-            if not "language" in self.cfg.user_form_remove:
-                self.make_row(_('Preferred language'), [self._lang_select()])
-
-            # boolean user options
-            bool_options = []
-            checkbox_fields = self.cfg.user_checkbox_fields
-            _ = self.request.getText
-            checkbox_fields.sort(lambda a, b: cmp(a[1](_), b[1](_)))
-            for key, label in checkbox_fields:
-                if not key in self.cfg.user_checkbox_remove:
-                    bool_options.extend([
-                        html.INPUT(type="checkbox", name=key, value="1",
-                            checked=getattr(self.request.user, key, 0),
-                            disabled=key in self.cfg.user_checkbox_disable and True or None),
-                        ' ', label(_), html.BR(),
-                    ])
-            self.make_row(_('General options'), bool_options, valign="top")
-
-            self.make_row(_('Quick links'), [
-                html.TEXTAREA(name="quicklinks", rows="6", cols="50")
-                    .append('\n'.join(self.request.user.getQuickLinks())),
-            ], valign="top")
-
-            # FIXME: this depends on Jabber ATM, but may not do so in the future
-            if self.cfg.jabber_enabled:
-                self.make_row(_('Subscribed events'), [self._event_select()])
-
-            # subscribed pages
-            if self.cfg.mail_enabled:
-                # Get list of subscribe pages, DO NOT sort! it should
-                # stay in the order the user entered it in his input
-                # box.
-                notifylist = self.request.user.getSubscriptionList()
-
-                warning = []
-                if not self.request.user.email:
-                    warning = [
-                        html.BR(),
-                        html.SMALL(Class="warning").append(
-                            _("This list does not work, unless you have"
-                              " entered a valid email address!")
-                        )]
-
-                self.make_row(
-                    html.Raw(_('Subscribed wiki pages (one regex per line)')),
-                    [html.TEXTAREA(name="subscribed_pages", rows="6", cols="50").append(
-                        '\n'.join(notifylist)),
-                    ] + warning,
-                    valign="top"
-                )
-        else: # not logged in
-            # Login / register interface
-            buttons = [
-                # IMPORTANT: login should be first to be the default
-                # button when a user hits ENTER.
-                #('login', _('Login')),  # we now have a Login macro
-                ('create', _('Create Profile')),
-                ('cancel', _('Cancel')),
-            ]
-            for key, label, type, length, textafter in self.cfg.user_form_fields:
-                if key in ('name', 'password', 'password2', 'email'):
-                    self.make_row(_(label),
-                              [html.INPUT(type=type, size=length, name=key,
-                                          value=''),
-                               ' ', _(textafter), ])
-
-        if self.cfg.mail_enabled:
-            buttons.append(("account_sendmail", _('Mail me my account data')))
-
-        if self.cfg.jabber_enabled:
-            buttons.append(("account_sendjabber", _('Send me my account data with Jabber')))
-
-        if create_only:
-            buttons = [("create_only", _('Create Profile'))]
-            if self.cfg.mail_enabled:
-                buttons.append(("create_and_mail", "%s + %s" %
-                                (_('Create Profile'), _('Email'))))
-
-        # Add buttons
-        button_cell = []
-        for name, label in buttons:
-            if not name in self.cfg.user_form_remove:
-                button_cell.extend([
-                    html.INPUT(type="submit", name=name, value=label),
-                    ' ',
-                ])
-        self.make_row('', button_cell)
-
-        return superuserform + unicode(self._form)
-
-
-def getUserForm(request, create_only=False):
-    """ Return HTML code for the user settings. """
-    return UserSettings(request).asHTML(create_only=create_only)
-
-
-class Login:
-    """ User login. """
-
-    def __init__(self, request):
-        """ Initialize user settings form.
-        """
-        self.request = request
-        self._ = request.getText
-        self.cfg = request.cfg
-
-    def make_row(self, label, cell, **kw):
-        """ Create a row in the form table.
-        """
-        self._table.append(html.TR().extend([
-            html.TD(**kw).extend([html.B().append(label), '   ']),
-            html.TD().extend(cell),
-        ]))
-
-
-    def asHTML(self):
-        """ Create the complete HTML form code. """
-        _ = self._
-        request = self.request
-        sn = request.getScriptname()
-        pi = request.getPathinfo()
-        action = u"%s%s" % (sn, pi)
-        userprefslink = wikiutil.getLocalizedPage(request, "UserPreferences").link_to(request, rel='nofollow')
-        sendmypasswordlink = wikiutil.getLocalizedPage(request, "SendMyPassword").link_to(request, rel='nofollow')
-        hint = _("To create an account, see the %(userprefslink)s page. To recover a lost password, go to %(sendmypasswordlink)s.") % {
-                 'userprefslink': userprefslink,
-                 'sendmypasswordlink': sendmypasswordlink}
-        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))
-
-        self._form.append(html.INPUT(type="hidden", name="action", value="login"))
-        self._form.append(self._table)
-        self._form.append(html.P().append(hint))
-        self._form.append(html.Raw("</div>"))
-
-        cfg = request.cfg
-        if 'username' in cfg.auth_login_inputs:
-            self.make_row(_('Name'), [
-                html.INPUT(
-                    type="text", size="32", name="name",
-                ),
-            ])
-
-        if 'password' in cfg.auth_login_inputs:
-            self.make_row(_('Password'), [
-                html.INPUT(
-                    type="password", size="32", name="password",
-                ),
-            ])
-
-        self.make_row('', [
-            html.INPUT(
-                type="submit", name='login', value=_('Login')
-            ),
-        ])
-
-        return unicode(self._form)
-
-def getLogin(request):
-    """ Return HTML code for the login. """
-    return Login(request).asHTML()
-
-#############################################################################
-### User account administration
-#############################################################################
-
-def do_user_browser(request):
-    """ Browser for SystemAdmin macro. """
-    from MoinMoin.util.dataset import TupleDataset, Column
-    from MoinMoin.Page import Page
-    _ = request.getText
-
-    data = TupleDataset()
-    data.columns = [
-        #Column('id', label=('ID'), align='right'),
-        Column('name', label=('Username')),
-        Column('email', label=('Email')),
-        Column('jabber', label=('Jabber')),
-        Column('action', label=_('Action')),
-    ]
-
-    # Iterate over users
-    for uid in user.getUserList(request):
-        account = user.User(request, uid)
-
-        userhomepage = Page(request, account.name)
-        if userhomepage.exists():
-            namelink = userhomepage.link_to(request)
-        else:
-            namelink = account.name
-
-        data.addRow((
-            #request.formatter.code(1) + uid + request.formatter.code(0),
-            # 0
-            request.formatter.rawHTML(namelink),
-            # 1
-            (request.formatter.url(1, 'mailto:' + account.email, css='mailto', do_escape=0) +
-             request.formatter.text(account.email) +
-             request.formatter.url(0)),
-            # 2
-            (request.formatter.url(1, 'xmpp:' + account.jid, css='mailto', do_escape=0) +
-             request.formatter.text(account.jid) +
-             request.formatter.url(0)),
-            # 3
-            (request.page.link_to(request, text=_('Mail me my account data'),
-                                 querystr={"action": "userform",
-                                           "email": account.email,
-                                           "account_sendmail": "1",
-                                           "sysadm": "users", },
-                                 rel='nofollow')
-            + " " +
-            request.page.link_to(request, text=_('Send me my account data with Jabber'),
-                                 querystr={"action": "userform",
-                                           "jid": account.jid,
-                                           "account_sendjabber": "1",
-                                           "sysadm": "users", },
-                                  rel='nofollow'))
-        ))
-
-    if data:
-        from MoinMoin.widget.browser import DataBrowserWidget
-
-        browser = DataBrowserWidget(request)
-        browser.setData(data)
-        return browser.toHTML()
-
-    # No data
-    return ''
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/userform/__init__.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,17 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - UserPreferences Form and User Browser
+
+    @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
+                2003-2007 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import time
+from MoinMoin import user, util, wikiutil
+import MoinMoin.events as events
+from MoinMoin.widget import html
+
+# compatibility
+from MoinMoin.userform.login import getLogin
+from MoinMoin.userform.admin import do_user_browser
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/userform/admin.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,67 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - User account administration
+
+    @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
+                2003-2007 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin import user
+from MoinMoin.util.dataset import TupleDataset, Column
+from MoinMoin.Page import Page
+
+
+def do_user_browser(request):
+    """ Browser for SystemAdmin macro. """
+    _ = request.getText
+
+    data = TupleDataset()
+    data.columns = [
+        #Column('id', label=('ID'), align='right'),
+        Column('name', label=('Username')),
+        Column('email', label=('Email')),
+        Column('jabber', label=('Jabber')),
+        Column('action', label=_('Action')),
+    ]
+
+    # Iterate over users
+    for uid in user.getUserList(request):
+        account = user.User(request, uid)
+
+        userhomepage = Page(request, account.name)
+        if userhomepage.exists():
+            namelink = userhomepage.link_to(request)
+        else:
+            namelink = account.name
+
+        data.addRow((
+            #request.formatter.code(1) + uid + request.formatter.code(0),
+            # 0
+            request.formatter.rawHTML(namelink),
+            # 1
+            (request.formatter.url(1, 'mailto:' + account.email, css='mailto', do_escape=0) +
+             request.formatter.text(account.email) +
+             request.formatter.url(0)),
+            # 2
+            (request.formatter.url(1, 'xmpp:' + account.jid, css='mailto', do_escape=0) +
+             request.formatter.text(account.jid) +
+             request.formatter.url(0)),
+            # 3
+            (request.page.link_to(request, text=_('Mail account data'),
+                                 querystr={"action": "recoverpass",
+                                           "email": account.email,
+                                           "account_sendmail": "1",
+                                           "sysadm": "users", },
+                                 rel='nofollow'))
+        ))
+
+    if data:
+        from MoinMoin.widget.browser import DataBrowserWidget
+
+        browser = DataBrowserWidget(request)
+        browser.setData(data)
+        return browser.toHTML()
+
+    # No data
+    return ''
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/userform/login.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,83 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Login form
+
+    @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
+                2003-2007 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import wikiutil
+from MoinMoin.widget import html
+
+
+class Login:
+    """ User login. """
+
+    def __init__(self, request):
+        """ Initialize user settings form.
+        """
+        self.request = request
+        self._ = request.getText
+        self.cfg = request.cfg
+
+    def make_row(self, label, cell, **kw):
+        """ Create a row in the form table.
+        """
+        self._table.append(html.TR().extend([
+            html.TD(**kw).extend([html.B().append(label), '   ']),
+            html.TD().extend(cell),
+        ]))
+
+
+    def asHTML(self):
+        """ Create the complete HTML form code. """
+        _ = self._
+        request = self.request
+        sn = request.getScriptname()
+        pi = request.getPathinfo()
+        action = u"%s%s" % (sn, pi)
+        userprefslink = request.page.url(request, querystr={'action': 'newaccount'})
+        sendmypasswordlink = request.page.url(request, querystr={'action': 'recoverpass'})
+        hint = _('If you do not have an account, <a href="%(userprefslink)s">you can create one now</a>. '
+                 '<a href="%(sendmypasswordlink)s">Forgot your password?</a>', formatted=False) % {
+                 'userprefslink': userprefslink,
+                 'sendmypasswordlink': sendmypasswordlink}
+        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))
+
+        self._form.append(html.INPUT(type="hidden", name="action", value="login"))
+        self._form.append(self._table)
+        self._form.append(html.P().append(html.Raw(hint)))
+        self._form.append(html.Raw("</div>"))
+
+        cfg = request.cfg
+        if 'username' in cfg.auth_login_inputs:
+            self.make_row(_('Name'), [
+                html.INPUT(
+                    type="text", size="32", name="name",
+                ),
+            ])
+
+        if 'password' in cfg.auth_login_inputs:
+            self.make_row(_('Password'), [
+                html.INPUT(
+                    type="password", size="32", name="password",
+                ),
+            ])
+
+        self.make_row('', [
+            html.INPUT(
+                type="submit", name='login', value=_('Login')
+            ),
+        ])
+
+        return unicode(self._form)
+
+def getLogin(request):
+    """ Return HTML code for the login. """
+    return Login(request).asHTML()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/userprefs/__init__.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,62 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - User preferences implementation
+
+    See also MoinMoin/action/userprefs.py
+
+    @copyright: 2007 MoinMoin:Johannesberg
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin.util import pysupport
+
+
+# create a list of extension actions from the package directory
+modules = pysupport.getPackageModules(__file__)
+
+
+class UserPrefBase(object):
+    '''
+        Base class for Settings objections
+
+        To get a new page in the wiki settings, create
+        a new 'userprefs' plugin and in it declare a class
+        named 'Settings' that inherits from this class.
+    '''
+    def __init__(self, request):
+        '''
+            Initialise a settings object. This should set the
+            object's title (which is displayed in the list of
+            possible settings)
+        '''
+        self.request = request
+        self._ = request.getText
+        title = 'No name set'
+
+    def create_form(self):
+        '''
+            This method should return HTML code for at least
+            one form. Each created form *must* contained the
+            hidden fields
+              * action: set to "userprefs"
+              * handler: set to the plugin name
+        '''
+        raise NotImplementedError
+
+    def handle_form(self, request):
+        '''
+            When any of the created forms is submitted and the
+            hidden fields are set correctly (see create_form)
+            this method will be invoked to handle the user's
+            input. Note that GET requests are also handed to
+            this method, so if you require POST check that.
+        '''
+        raise NotImplementedError
+
+    def allowed(self):
+        '''
+            Not all preferences are applicable to all users,
+            this method is called to determine whether the
+            title should be listed or not and whether
+            submissions are accepted.
+        '''
+        return True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/userprefs/prefs.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,524 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - UserPreferences Form and User Browser
+
+    @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
+                2003-2007 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import time
+from MoinMoin import user, util, wikiutil, events
+from MoinMoin.widget import html
+from MoinMoin.userprefs import UserPrefBase
+
+
+#################################################################
+# This is a mess.
+#
+# This plugin is also used by the 'recoverpass' and 'newaccount'
+# actions, and really shouldn't be.
+#
+# The plan for refactoring would be:
+#  1. make the mentioned actions create their own forms and not
+#     use the code here
+#  2. split the plugin into multiple preferences pages:
+#    - account details (name, email, timezone, ...)
+#    - change password
+#    - wiki settings (editor, fancy diffs, theme, ...)
+#    - notification settings (trivial, subscribed pages, ...)
+#    - quick links (or leave in wiki settings?)
+####
+
+
+_debug = 0
+
+class Settings(UserPrefBase):
+    def __init__(self, request):
+        """ Initialize user settings form. """
+        UserPrefBase.__init__(self, request)
+        self.request = request
+        self._ = request.getText
+        self.cfg = request.cfg
+        self.title = self._("Preferences")
+
+    def _decode_pagelist(self, key):
+        """ Decode list of pages from form input
+
+        Each line is a page name, empty lines ignored.
+
+        @param key: the form key to get
+        @rtype: list of unicode strings
+        @return: list of normalized names
+        """
+        text = self.request.form.get(key, [''])[0]
+        text = text.replace('\r', '')
+        items = []
+        for item in text.split('\n'):
+            item = item.strip()
+            if not item:
+                continue
+            items.append(item)
+        return items
+
+    def _save_user_prefs(self):
+        _ = self._
+        form = self.request.form
+
+        if self.request.request_method != 'POST':
+            return _("Use UserPreferences to change your settings or create an account.")
+        theuser = self.request.user
+        if not theuser:
+            return
+
+        if not 'name' in theuser.auth_attribs:
+            # Require non-empty name
+            theuser.name = form.get('name', [theuser.name])[0]
+
+            # Don't allow changing the name to an invalid one
+            if not user.isValidName(self.request, theuser.name):
+                return _("""Invalid user name {{{'%s'}}}.
+Name may contain any Unicode alpha numeric character, with optional one
+space between words. Group page name is not allowed.""") % wikiutil.escape(theuser.name)
+
+            # Is this an existing user trying to change information or a new user?
+            # Name required to be unique. Check if name belong to another user.
+            if user.getUserId(self.request, theuser.name):
+                if theuser.name != self.request.user.name:
+                    return _("This user name already belongs to somebody else.")
+
+            if not theuser.name:
+                return _("Empty user name. Please enter a user name.")
+
+        if not 'password' in theuser.auth_attribs:
+            # try to get the password and pw repeat
+            password = form.get('password', [''])[0]
+            password2 = form.get('password2', [''])[0]
+
+            # Check if password is given and matches with password repeat
+            if password != password2:
+                return _("Passwords don't match!")
+
+            # Encode password
+            if password and not password.startswith('{SHA}'):
+                try:
+                    theuser.enc_password = user.encodePassword(password)
+                except UnicodeError, err:
+                    # Should never happen
+                    return "Can't encode password: %s" % str(err)
+
+        if not 'email' in theuser.auth_attribs:
+            # try to get the email
+            email = wikiutil.clean_input(form.get('email', [theuser.email])[0])
+            theuser.email = email.strip()
+
+            # Require email
+            if not theuser.email:
+                return _("Please provide your email address. If you lose your"
+                         " login information, you can get it by email.")
+
+            # Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
+            if theuser.email and self.request.cfg.user_email_unique:
+                other = user.get_by_email_address(self.request, theuser.email)
+                if other is not None and other.id != theuser.id:
+                    return _("This email already belongs to somebody else.")
+
+        if not 'jid' in theuser.auth_attribs:
+            # try to get the jid
+            jid = wikiutil.clean_input(form.get('jid', "")).strip()
+
+            jid_changed = theuser.jid != jid
+            previous_jid = theuser.jid
+            theuser.jid = jid
+
+            if theuser.jid and self.request.cfg.user_jid_unique:
+                other = user.get_by_jabber_id(self.request, theuser.jid)
+                if other is not None and other.id != theuser.id:
+                    return _("This jabber id already belongs to somebody else.")
+
+            if jid_changed:
+                set_event = events.JabberIDSetEvent(self.request, theuser.jid)
+                unset_event = events.JabberIDUnsetEvent(self.request, previous_jid)
+                events.send_event(unset_event)
+                events.send_event(set_event)
+
+        if not 'aliasname' in theuser.auth_attribs:
+            # aliasname
+            theuser.aliasname = wikiutil.clean_input(form.get('aliasname', [''])[0])
+
+        # editor size
+        theuser.edit_rows = util.web.getIntegerInput(self.request, 'edit_rows', theuser.edit_rows, 10, 60)
+
+        # try to get the editor
+        theuser.editor_default = form.get('editor_default', [self.cfg.editor_default])[0]
+        theuser.editor_ui = form.get('editor_ui', [self.cfg.editor_ui])[0]
+
+        # time zone
+        theuser.tz_offset = util.web.getIntegerInput(self.request, 'tz_offset', theuser.tz_offset, -84600, 84600)
+
+        # datetime format
+        try:
+            dt_d_combined = Settings._date_formats.get(form['datetime_fmt'][0], '')
+            theuser.datetime_fmt, theuser.date_fmt = dt_d_combined.split(' & ')
+        except (KeyError, ValueError):
+            theuser.datetime_fmt = '' # default
+            theuser.date_fmt = '' # default
+
+        # try to get the (optional) theme
+        theme_name = form.get('theme_name', [self.cfg.theme_default])[0]
+        if theme_name != theuser.theme_name:
+            # if the theme has changed, load the new theme
+            # so the user has a direct feedback
+            # WARNING: this should be refactored (i.e. theme load
+            # after userform handling), cause currently the
+            # already loaded theme is just replaced (works cause
+            # nothing has been emitted yet)
+            theuser.theme_name = theme_name
+            if self.request.loadTheme(theuser.theme_name) > 0:
+                theme_name = wikiutil.escape(theme_name)
+                return _("The theme '%(theme_name)s' could not be loaded!") % locals()
+
+        # try to get the (optional) preferred language
+        theuser.language = form.get('language', [''])[0]
+
+        # I want to handle all inputs from user_form_fields, but
+        # don't want to handle the cases that have already been coded
+        # above.
+        # This is a horribly fragile kludge that's begging to break.
+        # Something that might work better would be to define a
+        # handler for each form field, instead of stuffing them all in
+        # one long and inextensible method.  That would allow for
+        # plugins to provide methods to validate their fields as well.
+        already_handled = ['name', 'password', 'password2', 'email',
+                           'aliasname', 'edit_rows', 'editor_default',
+                           'editor_ui', 'tz_offset', 'datetime_fmt',
+                           'theme_name', 'language', 'jid']
+        for field in self.cfg.user_form_fields:
+            key = field[0]
+            if ((key in self.cfg.user_form_disable)
+                or (key in already_handled)):
+                continue
+            default = self.cfg.user_form_defaults[key]
+            value = form.get(key, [default])[0]
+            setattr(theuser, key, value)
+
+        # checkbox options
+        for key, label in self.cfg.user_checkbox_fields:
+            if key not in self.cfg.user_checkbox_disable and key not in self.cfg.user_checkbox_remove:
+                value = form.get(key, ["0"])[0]
+                try:
+                    value = int(value)
+                except ValueError:
+                    pass
+                else:
+                    setattr(theuser, key, value)
+
+        # quicklinks for navibar
+        theuser.quicklinks = self._decode_pagelist('quicklinks')
+
+        # subscription for page change notification
+        theuser.subscribed_pages = self._decode_pagelist('subscribed_pages')
+
+        # subscription to various events
+        available = events.get_subscribable_events()
+        theuser.subscribed_events = [ev for ev in form.get('events', [])]
+
+        # save data
+        theuser.save()
+        self.request.user = theuser
+
+        result = _("User preferences saved!")
+        if _debug:
+            result = result + util.dumpFormData(form)
+        return result
+
+
+    def handle_form(self):
+        _ = self._
+        form = self.request.form
+
+        if form.has_key('cancel'):
+            return
+
+        if form.has_key('save'): # Save user profile
+            return self._save_user_prefs()
+
+    # form generation part
+
+    _date_formats = { # datetime_fmt & date_fmt
+        'iso': '%Y-%m-%d %H:%M:%S & %Y-%m-%d',
+        'us': '%m/%d/%Y %I:%M:%S %p & %m/%d/%Y',
+        'euro': '%d.%m.%Y %H:%M:%S & %d.%m.%Y',
+        'rfc': '%a %b %d %H:%M:%S %Y & %a %b %d %Y',
+    }
+
+    def _tz_select(self):
+        """ Create time zone selection. """
+        tz = 0
+        if self.request.user.valid:
+            tz = int(self.request.user.tz_offset)
+
+        options = []
+        now = time.time()
+        for halfhour in range(-47, 48):
+            offset = halfhour * 1800
+            t = now + offset
+
+            options.append((
+                str(offset),
+                '%s [%s%s:%s]' % (
+                    time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple(t)),
+                    "+-"[offset < 0],
+                    "%02d" % (abs(offset) / 3600),
+                    "%02d" % (abs(offset) % 3600 / 60),
+                ),
+            ))
+
+        return util.web.makeSelection('tz_offset', options, str(tz))
+
+
+    def _dtfmt_select(self):
+        """ Create date format selection. """
+        _ = self._
+        try:
+            dt_d_combined = '%s & %s' % (self.request.user.datetime_fmt, self.request.user.date_fmt)
+            selected = [
+                k for k, v in self._date_formats.items()
+                    if v == dt_d_combined][0]
+        except IndexError:
+            selected = ''
+        options = [('', _('Default'))] + self._date_formats.items()
+
+        return util.web.makeSelection('datetime_fmt', options, selected)
+
+
+    def _lang_select(self):
+        """ Create language selection. """
+        from MoinMoin import i18n
+        _ = self._
+        cur_lang = self.request.user.valid and self.request.user.language or ''
+        langs = i18n.wikiLanguages().items()
+        langs.sort(lambda x, y: cmp(x[1]['x-language'], y[1]['x-language']))
+        options = [('', _('<Browser setting>', formatted=False))]
+        for lang in langs:
+            name = lang[1]['x-language']
+            options.append((lang[0], name))
+
+        return util.web.makeSelection('language', options, cur_lang)
+
+    def _theme_select(self):
+        """ Create theme selection. """
+        cur_theme = self.request.user.valid and self.request.user.theme_name or self.cfg.theme_default
+        options = [("<default>", "<%s>" % self._("Default"))]
+        for theme in wikiutil.getPlugins('theme', self.request.cfg):
+            options.append((theme, theme))
+
+        return util.web.makeSelection('theme_name', options, cur_theme)
+
+    def _event_select(self):
+        """ Create event subscription list. """
+
+        event_list = events.get_subscribable_events()
+        selected = self.request.user.subscribed_events
+        super = self.request.user.isSuperUser()
+
+        # Create a list of (value, name) tuples for display in <select>
+        # Only include super-user visible events if current user has these rights.
+        # It's cosmetic - the check for super-user rights should be performed
+        # in event handling code as well!
+        allowed = []
+        for key in event_list.keys():
+            if not event_list[key]['superuser'] or super:
+                allowed.append((key, event_list[key]['desc']))
+
+        return util.web.makeMultiSelection('events', allowed, selectedvals=selected)
+
+    def _editor_default_select(self):
+        """ Create editor selection. """
+        editor_default = self.request.user.valid and self.request.user.editor_default or self.cfg.editor_default
+        options = [("<default>", "<%s>" % self._("Default"))]
+        for editor in ['text', 'gui', ]:
+            options.append((editor, editor))
+        return util.web.makeSelection('editor_default', options, editor_default)
+
+    def _editor_ui_select(self):
+        """ Create editor selection. """
+        editor_ui = self.request.user.valid and self.request.user.editor_ui or self.cfg.editor_ui
+        options = [("<default>", "<%s>" % self._("Default")),
+                   ("theonepreferred", self._("the one preferred")),
+                   ("freechoice", self._("free choice")),
+                  ]
+        return util.web.makeSelection('editor_ui', options, editor_ui)
+
+    def _make_form(self):
+        """ Create the FORM, and the TABLE with the input fields
+        """
+        sn = self.request.getScriptname()
+        pi = self.request.getPathinfo()
+        action = u"%s%s" % (sn, pi)
+        self._form = html.FORM(action=action)
+        self._table = html.TABLE(border="0")
+
+        # Use the user interface language and direction
+        lang_attr = self.request.theme.ui_lang_attr()
+        self._form.append(html.Raw('<div class="userpref"%s>' % lang_attr))
+
+        self._form.append(self._table)
+        self._form.append(html.Raw("</div>"))
+
+
+    def make_row(self, label, cell, **kw):
+        """ Create a row in the form table.
+        """
+        self._table.append(html.TR().extend([
+            html.TD(**kw).extend([html.B().append(label), '   ']),
+            html.TD().extend(cell),
+        ]))
+
+
+    def create_form(self, create_only=False, recover_only=False):
+        """ Create the complete HTML form code. """
+        _ = self._
+        self._make_form()
+
+        if self.request.user.valid and not create_only and not recover_only:
+            buttons = [('save', _('Save')), ('cancel', _('Cancel')), ]
+            uf_remove = self.cfg.user_form_remove
+            uf_disable = self.cfg.user_form_disable
+            for attr in self.request.user.auth_attribs:
+                if attr == 'password':
+                    uf_remove.append(attr)
+                    uf_remove.append('password2')
+                else:
+                    uf_disable.append(attr)
+            for key, label, type, length, textafter in self.cfg.user_form_fields:
+                default = self.cfg.user_form_defaults[key]
+                if not key in uf_remove:
+                    if key in uf_disable:
+                        self.make_row(_(label),
+                                  [html.INPUT(type=type, size=length, name=key, disabled="disabled",
+                                   value=getattr(self.request.user, key)), ' ', _(textafter), ])
+                    else:
+                        self.make_row(_(label),
+                                  [html.INPUT(type=type, size=length, name=key, value=getattr(self.request.user, key)), ' ', _(textafter), ])
+
+            if not self.cfg.theme_force and not "theme_name" in self.cfg.user_form_remove:
+                self.make_row(_('Preferred theme'), [self._theme_select()])
+
+            if not self.cfg.editor_force:
+                if not "editor_default" in self.cfg.user_form_remove:
+                    self.make_row(_('Editor Preference'), [self._editor_default_select()])
+                if not "editor_ui" in self.cfg.user_form_remove:
+                    self.make_row(_('Editor shown on UI'), [self._editor_ui_select()])
+
+            if not "tz_offset" in self.cfg.user_form_remove:
+                self.make_row(_('Time zone'), [
+                    _('Your time is'), ' ',
+                    self._tz_select(),
+                    html.BR(),
+                    _('Server time is'), ' ',
+                    time.strftime(self.cfg.datetime_fmt, util.timefuncs.tmtuple()),
+                    ' (UTC)',
+                ])
+
+            if not "datetime_fmt" in self.cfg.user_form_remove:
+                self.make_row(_('Date format'), [self._dtfmt_select()])
+
+            if not "language" in self.cfg.user_form_remove:
+                self.make_row(_('Preferred language'), [self._lang_select()])
+
+            # boolean user options
+            bool_options = []
+            checkbox_fields = self.cfg.user_checkbox_fields
+            _ = self.request.getText
+            checkbox_fields.sort(lambda a, b: cmp(a[1](_), b[1](_)))
+            for key, label in checkbox_fields:
+                if not key in self.cfg.user_checkbox_remove:
+                    bool_options.extend([
+                        html.INPUT(type="checkbox", name=key, value="1",
+                            checked=getattr(self.request.user, key, 0),
+                            disabled=key in self.cfg.user_checkbox_disable and True or None),
+                        ' ', label(_), html.BR(),
+                    ])
+            self.make_row(_('General options'), bool_options, valign="top")
+
+            self.make_row(_('Quick links'), [
+                html.TEXTAREA(name="quicklinks", rows="6", cols="50")
+                    .append('\n'.join(self.request.user.getQuickLinks())),
+            ], valign="top")
+
+            # FIXME: this depends on Jabber ATM, but may not do so in the future
+            if self.cfg.jabber_enabled:
+                self.make_row(_('Subscribed events'), [self._event_select()])
+
+            # subscribed pages
+            if self.cfg.mail_enabled:
+                # Get list of subscribe pages, DO NOT sort! it should
+                # stay in the order the user entered it in his input
+                # box.
+                notifylist = self.request.user.getSubscriptionList()
+
+                warning = []
+                if not self.request.user.email:
+                    warning = [
+                        html.BR(),
+                        html.SMALL(Class="warning").append(
+                            _("This list does not work, unless you have"
+                              " entered a valid email address!")
+                        )]
+
+                self.make_row(
+                    html.Raw(_('Subscribed wiki pages (one regex per line)')),
+                    [html.TEXTAREA(name="subscribed_pages", rows="6", cols="50").append(
+                        '\n'.join(notifylist)),
+                    ] + warning,
+                    valign="top"
+                )
+            self._form.append(html.INPUT(type="hidden", name="action", value="userprefs"))
+            self._form.append(html.INPUT(type="hidden", name="handler", value="prefs"))
+        elif not recover_only:
+            # Login / register interface
+            buttons = [
+                # IMPORTANT: login should be first to be the default
+                # button when a user hits ENTER.
+                #('login', _('Login')),  # we now have a Login macro
+                ('create', _('Create Profile')),
+                ('cancel', _('Cancel')),
+            ]
+            for key, label, type, length, textafter in self.cfg.user_form_fields:
+                if key in ('name', 'password', 'password2', 'email'):
+                    self.make_row(_(label),
+                              [html.INPUT(type=type, size=length, name=key,
+                                          value=''),
+                               ' ', _(textafter), ])
+            self._form.append(html.INPUT(type="hidden", name="action", value="newaccount"))
+        else:
+            for key, label, type, length, textafter in self.cfg.user_form_fields:
+                if key == 'email':
+                    self.make_row(_(label),
+                              [html.INPUT(type=type, size=length, name=key,
+                                          value=''),
+                               ' ', _(textafter), ])
+            buttons = []
+            self._form.append(html.INPUT(type="hidden", name="action", value="recoverpass"))
+
+        if recover_only and self.cfg.mail_enabled:
+            buttons.append(("account_sendmail", _('Mail me my account data')))
+
+        if create_only:
+            buttons = [("create_only", _('Create Profile'))]
+            if self.cfg.mail_enabled:
+                buttons.append(("create_and_mail", "%s + %s" %
+                                (_('Create Profile'), _('Email'))))
+
+        # Add buttons
+        button_cell = []
+        for name, label in buttons:
+            if not name in self.cfg.user_form_remove:
+                button_cell.extend([
+                    html.INPUT(type="submit", name=name, value=label),
+                    ' ',
+                ])
+        self.make_row('', button_cell)
+
+        return unicode(self._form)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/userprefs/suid.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,129 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - switch user form
+
+    @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
+                2003-2007 MoinMoin:ThomasWaldmann
+                2007      MoinMoin:JohannesBerg
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import user, util, wikiutil
+from MoinMoin.widget import html
+from MoinMoin.userprefs import UserPrefBase
+
+class Settings(UserPrefBase):
+    def __init__(self, request):
+        """ Initialize setuid settings form. """
+        UserPrefBase.__init__(self, request)
+        self.request = request
+        self._ = request.getText
+        self.cfg = request.cfg
+        self.title = self._("Switch user")
+
+    def _is_super_user(self):
+        return (self.request.user.isSuperUser() or
+                (not self.request._setuid_real_user is None
+                 and (self.request._setuid_real_user.isSuperUser())))
+
+    allowed = _is_super_user
+
+    def handle_form(self):
+        _ = self._
+        form = self.request.form
+
+        if form.has_key('cancel'):
+            return
+
+        if (wikiutil.checkTicket(self.request, self.request.form['ticket'][0]) and
+            self.request.request_method == 'POST' and self._is_super_user()):
+            su_user = form.get('selected_user', [''])[0]
+            uid = user.getUserId(self.request, su_user)
+            if not uid:
+                return _("No user selected")
+            if (not self.request._setuid_real_user is None
+                and uid == self.request._setuid_real_user.id):
+                del self.request.session['setuid']
+                self.request.user = self.request._setuid_real_user
+                self.request._setuid_real_user = None
+            else:
+                theuser = user.User(self.request, uid, auth_method='setuid')
+                theuser.disabled = None
+                self.request.session['setuid'] = uid
+                self.request._setuid_real_user = self.request.user
+                # now continue as the other user
+                self.request.user = theuser
+            return  _("Use UserPreferences to change settings of the selected user account, log out to get back to your account.")
+        else:
+            return _("Use UserPreferences to change your settings or create an account.")
+
+
+    def _user_select(self):
+        options = []
+        users = user.getUserList(self.request)
+        realuid = None
+        if hasattr(self.request, '_setuid_real_user') and self.request._setuid_real_user:
+            realuid = self.request._setuid_real_user.id
+        else:
+            realuid = self.request.user.id
+        for uid in users:
+            if uid != realuid:
+                name = user.User(self.request, id=uid).name # + '/' + uid # for debugging
+                options.append((name, name))
+        options.sort()
+
+        size = min(5, len(options))
+        current_user = self.request.user.name
+        return util.web.makeSelection('selected_user', options, current_user, size=size)
+
+    def _make_form(self):
+        """ Create the FORM, and the TABLE with the input fields
+        """
+        sn = self.request.getScriptname()
+        pi = self.request.getPathinfo()
+        action = u"%s%s" % (sn, pi)
+        self._form = html.FORM(action=action)
+        self._table = html.TABLE(border="0")
+
+        # Use the user interface language and direction
+        lang_attr = self.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="userprefs"))
+        self._form.append(html.INPUT(type="hidden", name="handler", value="suid"))
+        self._form.append(self._table)
+        self._form.append(html.Raw("</div>"))
+
+
+    def _make_row(self, label, cell, **kw):
+        """ Create a row in the form table.
+        """
+        self._table.append(html.TR().extend([
+            html.TD(**kw).extend([html.B().append(label), '   ']),
+            html.TD().extend(cell),
+        ]))
+
+
+    def create_form(self):
+        """ Create the complete HTML form code. """
+        _ = self._
+        self._make_form()
+
+        if (self.request.user.isSuperUser() or
+            (not self.request._setuid_real_user is None and
+             self.request._setuid_real_user.isSuperUser())):
+            ticket = wikiutil.createTicket(self.request)
+            self._make_row(_('Select User'), [self._user_select()])
+            self._form.append(html.INPUT(type="hidden", name="ticket", value="%s" % ticket))
+            buttons = [("select_user", _('Select User')),
+                       ("cancel", _('Cancel'))]
+            button_cell = []
+            for name, label in buttons:
+                button_cell.extend([
+                    html.INPUT(type="submit", name=name, value=label),
+                    ' ',
+                ])
+            self._make_row('', button_cell)
+            return unicode(self._form)
+
+        return u''
--- a/docs/CHANGES	Tue Jul 10 17:57:58 2007 +0200
+++ b/docs/CHANGES	Tue Jul 10 17:58:14 2007 +0200
@@ -40,6 +40,7 @@
     * cfg.trusted_auth_methods
     * new session handling system
     * new authentication plugin system
+    * new preferences plugin system, see MoinMoin/userprefs/__init__.py
 
   Bugfixes:
     * ...
@@ -47,7 +48,7 @@
   Other changes:
     * cfg.show_login is gone, see code in theme/__init__.py, this affects
       many themes!
-
+    * needs a new userprefs/ plugin directory
 
 Version 1.6.current:
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wiki/data/plugin/userprefs/__init__.py	Tue Jul 10 17:58:14 2007 +0200
@@ -0,0 +1,5 @@
+# -*- coding: iso-8859-1 -*-
+
+from MoinMoin.util import pysupport
+
+modules = pysupport.getPackageModules(__file__)