changeset 3335:4bccb8c29219

merged main
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Wed, 19 Mar 2008 13:22:25 +0100
parents 3f61d2b76e23 (current diff) 928a45b60bb3 (diff)
children fb0f0f3ef115
files
diffstat 24 files changed, 260 insertions(+), 174 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/action/newaccount.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/action/newaccount.py	Wed Mar 19 13:22:25 2008 +0100
@@ -19,7 +19,7 @@
     form = request.form
 
     if request.request_method != 'POST':
-        return _("Use UserPreferences to change your settings or create an account.", wiki=True)
+        return
 
     if not TextCha(request).check_answer_from_form():
         return _('TextCha: Wrong answer! Go back and try again...')
--- a/MoinMoin/action/revert.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/action/revert.py	Wed Mar 19 13:22:25 2008 +0100
@@ -10,6 +10,10 @@
 def execute(pagename, request):
     """ restore another revision of a page as a new current revision """
     from MoinMoin.PageEditor import PageEditor
+
+    if request.request_method != 'POST':
+        return
+
     rev = request.rev
     pg = PageEditor(request, pagename)
 
--- a/MoinMoin/action/subscribe.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/action/subscribe.py	Wed Mar 19 13:22:25 2008 +0100
@@ -26,8 +26,8 @@
 
     # Suggest users without email to add their email address
     elif not request.user.email and not request.user.jid:
-        request.theme.add_msg(_("Add your email address or Jabber ID in your UserPreferences to use subscriptions.",
-                                wiki=True), "error")
+        request.theme.add_msg(_("Add your email address or Jabber ID in your user settings to use subscriptions."),
+                              "error")
 
     elif request.user.isSubscribedTo([pagename]):
         request.theme.add_msg(_('You are already subscribed to this page.'))
--- a/MoinMoin/action/unsubscribe.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/action/unsubscribe.py	Wed Mar 19 13:22:25 2008 +0100
@@ -19,8 +19,7 @@
             msg = _('Your subscription to this page has been removed.')
         else:
             msg = _("Can't remove regular expression subscription!") + u' ' + \
-                  _("Edit the subscription regular expressions in your "
-                    "UserPreferences.", wiki=True)
+                  _("Edit the subscription regular expressions in your settings.")
     else:
         # The user is not subscribed
         msg = _('You need to be subscribed to unsubscribe.')
--- a/MoinMoin/action/userprefs.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/action/userprefs.py	Wed Mar 19 13:22:25 2008 +0100
@@ -1,11 +1,9 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - UserPreferences action
-
-    This is a simple plugin, that adds a "UserPreferences" action.
+    MoinMoin - user settings action
 
     @copyright: 2006 Radomir Dopieralski
-                2007 MoinMoin:JohannesBerg
+                2007, 2008 MoinMoin:JohannesBerg
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -85,12 +83,14 @@
 def execute(pagename, request):
     _ = request.getText
     text, title, msg = _create_page(request)
-    if not title:
-        title = _("Settings")
+    if title:
+        # XXX: we would like to make "Settings" here a link back
+        #      to the generic userprefs page but that is impossible
+        #      due to the way the title is emitted and the theme is
+        #      responsible for doing the linking....
+        title = _("Settings") + ":" + title
     else:
-        lnk = html.A(href='xx').append(html.Text(_("Settings")))
-        lnk = unicode(lnk)
-        title = _("Settings") + "/" + title
+        title = _("Settings")
     request.emit_http_headers()
     request.theme.add_msg(msg, "dialog")
     request.theme.send_title(title, page=request.page, pagename=pagename)
--- a/MoinMoin/auth/__init__.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/auth/__init__.py	Wed Mar 19 13:22:25 2008 +0100
@@ -115,7 +115,7 @@
     When creating a new MoinMoin.user.User object, you can give a keyword
     argument "auth_attribs" to User.__init__ containing a list of user
     attributes that are determined and fixed by this auth method and may
-    not be changed by the user in UserPreferences.
+    not be changed by the user in their preferences.
     You also have to give the keyword argument "auth_method" containing the
     name of the authentication method.
 
--- a/MoinMoin/config/_tests/test_multiconfig.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/config/_tests/test_multiconfig.py	Wed Mar 19 13:22:25 2008 +0100
@@ -40,24 +40,5 @@
                 print "%r: %s" % (pw, pw_error)
                 assert result == (pw_error is None)
 
-    def testCrackPasswordChecker(self):
-        pw_checker = self.request.cfg.password_checker
-        if not pw_checker:
-            py.test.skip("password_checker is disabled in the configuration, not testing it")
-        else:
-            try:
-                import crack # do we have python-crack?
-            except:
-                py.test.skip("python-crack is not installed")
-            try:
-                crack.FascistCheck("a12fv./ZX47") # this should not raise an exception
-            except:
-                py.test.skip("python-crack is not working correctly (did you forget to build the dicts?)")
-            else:
-                for pw, result in self.tests_crack:
-                    pw_error = pw_checker(self.username, pw)
-                    print "%r: %s" % (pw, pw_error)
-                    assert result == (pw_error is None)
-
 coverage_modules = ['MoinMoin.config.multiconfig']
 
--- a/MoinMoin/config/multiconfig.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/config/multiconfig.py	Wed Mar 19 13:22:25 2008 +0100
@@ -507,9 +507,11 @@
 
     def password_checker(username, password):
         """ Check if a password is secure enough.
-            First (and in any case), we use a built-in check to get rid of the
-            worst passwords. If there is cracklib installed, we use it for
-            additional checks.
+            We use a built-in check to get rid of the worst passwords.
+
+            We do NOT use cracklib / python-crack here any more because it is
+            not thread-safe (we experienced segmentation faults when using it).
+
             If you don't want to check passwords, use password_checker = None.
 
             @return: None if there is no problem with the password,
@@ -537,16 +539,6 @@
                 if password in kbd or password in rev_kbd or \
                    password_lower in kbd or password_lower in rev_kbd:
                     raise ValueError("Password too easy (kbd sequence)")
-            try:
-                # to use advanced checking, you need to install python-crack,
-                # cracklib-runtime (dict processing) and do not forget to
-                # initialize the crack dicts!
-                import crack
-                # instead of some "old password" we give the username to check
-                # whether the password is too similar to the username
-                crack.VeryFascistCheck(password, username) # raises ValueError on bad passwords
-            except ImportError:
-                pass
             return None
         except ValueError, err:
             return str(err)
--- a/MoinMoin/formatter/_tests/test_formatter.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/formatter/_tests/test_formatter.py	Wed Mar 19 13:22:25 2008 +0100
@@ -14,7 +14,6 @@
 
 class TestFormatter:
     def testSyntaxReferenceDomXml(self):
-        py.test.skip("dom_xml formatter is known to be broken")
         f_name = 'dom_xml'
         try:
             formatter = wikiutil.importPlugin(self.request.cfg, "formatter", f_name, "Formatter")
@@ -26,6 +25,7 @@
             print "Done."
 
     def testSyntaxReferenceDocBook(self):
+        py.test.skip("docbook is broken")
         try:
             from xml.dom import getDOMImplementation
             dom = getDOMImplementation("4DOM")
@@ -40,7 +40,7 @@
                 pass
             else:
                 print "Formatting using %r" % formatter
-                self.formatPage("SyntaxReference", formatter)
+                self.formatPage("HelpOnMoinWikiSyntax", formatter)
                 print "Done."
 
     def testSyntaxReferenceOthers(self):
@@ -127,7 +127,7 @@
                     'MoinMoin.formatter.text_xml',
                     'MoinMoin.formatter.text_docbook',
                     'MoinMoin.formatter.text_plain',
-                    #'MoinMoin.formatter.dom_xml',
+                    'MoinMoin.formatter.dom_xml',
                     'MoinMoin.formatter.text_python',
                     'MoinMoin.formatter.pagelinks',
                    ]
--- a/MoinMoin/formatter/text_html.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/formatter/text_html.py	Wed Mar 19 13:22:25 2008 +0100
@@ -1004,7 +1004,7 @@
         """
         if self._in_code_area:
             preformatted = 1
-        return ['\n', '<br />\n'][not preformatted] + self._indent_spaces()
+        return ['\n', '<br>\n'][not preformatted] + self._indent_spaces()
 
     def paragraph(self, on, **kw):
         """Creates a paragraph with a <p> element.
--- a/MoinMoin/i18n/de.MoinMoin.po	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/i18n/de.MoinMoin.po	Wed Mar 19 13:22:25 2008 +0100
@@ -1832,7 +1832,7 @@
 
 #, python-format
 msgid "attachment:%(filename)s of %(pagename)s"
-msgstr "[[Verbatim(attachment:)]]%(filename)s für %(pagename)s"
+msgstr "attachment:%(filename)s für %(pagename)s"
 
 msgid "This page is already deleted or was never created!"
 msgstr "Diese Seite wurde bereits gelöscht oder wurde bisher nicht angelegt!"
--- a/MoinMoin/i18n/dummy.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/i18n/dummy.py	Wed Mar 19 13:22:25 2008 +0100
@@ -16,7 +16,6 @@
 _('SiteNavigation')
 _('HelpContents')
 _('HelpOnFormatting')
-_('UserPreferences')
 _('SendMyPassword')
 _('WikiLicense')
 _('MissingPage')
--- a/MoinMoin/i18n/en.MoinMoin.po	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/i18n/en.MoinMoin.po	Wed Mar 19 13:22:25 2008 +0100
@@ -1506,13 +1506,13 @@
 msgid "Attachment '%(target)s' (remote name '%(filename)s') already exists."
 msgstr ""
 
-#, fuzzy, python-format
+#, python-format
 msgid "Attachment '%(filename)s' already exists."
-msgstr "[[Verbatim(attachment:)]]%(filename)s of %(pagename)s"
-
-#, fuzzy, python-format
+msgstr ""
+
+#, python-format
 msgid "Attachment '%(filename)s' moved to %(page)s."
-msgstr "[[Verbatim(attachment:)]]%(filename)s of %(pagename)s"
+msgstr ""
 
 msgid "Nothing changed"
 msgstr ""
@@ -1537,13 +1537,12 @@
 msgid "New page name"
 msgstr ""
 
-#, fuzzy
 msgid "New attachment name"
-msgstr "Attachments"
-
-#, fuzzy, python-format
+msgstr ""
+
+#, python-format
 msgid "Attachment '%(filename)s' installed."
-msgstr "[[Verbatim(attachment:)]]%(filename)s of %(pagename)s"
+msgstr ""
 
 #, python-format
 msgid ""
@@ -1557,9 +1556,9 @@
 "would be too many (%(count)d missing)."
 msgstr ""
 
-#, fuzzy, python-format
+#, python-format
 msgid "Attachment '%(filename)s' unzipped."
-msgstr "[[Verbatim(attachment:)]]%(filename)s of %(pagename)s"
+msgstr ""
 
 #, python-format
 msgid ""
@@ -1592,7 +1591,7 @@
 
 #, python-format
 msgid "attachment:%(filename)s of %(pagename)s"
-msgstr "[[Verbatim(attachment:)]]%(filename)s of %(pagename)s"
+msgstr ""
 
 msgid "This page is already deleted or was never created!"
 msgstr ""
--- a/MoinMoin/macro/MonthCalendar.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/macro/MonthCalendar.py	Wed Mar 19 13:22:25 2008 +0100
@@ -331,7 +331,7 @@
             cssday = "cal-weekend"
         else:
             cssday = "cal-workday"
-        restd2.append('  <td class="%s" width="14%%">%s</td>\n' % (cssday, wday))
+        restd2.append('  <td class="%s">%s</td>\n' % (cssday, wday))
     restr2 = ' <tr>\n%s </tr>\n' % "".join(restd2)
 
     if parmheight6:
@@ -401,19 +401,24 @@
                 restdn.append('  <td style="%s" class="%s">%s</td>\n' % (style, cssday, fmtlink))
         restrn.append(' <tr>\n%s </tr>\n' % "".join(restdn))
 
-    restable = '<table border="2" cellspacing="2" cellpadding="2">\n%s%s%s</table>\n'
+    restable = '<table border="2" cellspacing="2" cellpadding="2">\n<col width="14%%" span="7">%s%s%s</table>\n'
     restable = restable % (restr1, restr2, "".join(restrn))
 
-    result = """\
-<script language="JavaScript" type="text/javascript" src="%s/common/js/infobox.js"></script>
-<div id="infodiv" style="position:absolute; visibility:hidden; z-index:20; top:-999em; left:0px;"></div>
-<script language="JavaScript" type="text/javascript">
+    if maketip_js:
+        tip_js = '''<script language="JavaScript" type="text/javascript">
 <!--
 %s
 // -->
 </script>
-%s
-""" % (request.cfg.url_prefix_static, "\n".join(maketip_js), restable)
+''' % '\n'.join(maketip_js)
+    else:
+        tip_js = ''
+
+    result = """\
+<script type="text/javascript" src="%s/common/js/infobox.js"></script>
+<div id="%s" style="position:absolute; visibility:hidden; z-index:20; top:-999em; left:0px;"></div>
+%s%s
+""" % (request.cfg.url_prefix_static, formatter.make_id_unique('infodiv'), tip_js, restable)
     return formatter.rawHTML(result)
 
 # EOF
--- a/MoinMoin/parser/text_csv.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/parser/text_csv.py	Wed Mar 19 13:22:25 2008 +0100
@@ -1,74 +1,173 @@
-# -*- coding: iso-8859-1 -*-
+# -*- coding: utf-8 -*-
 """
     MoinMoin - Parser for CSV data
 
-    This parser lacks to flexibility to read arbitary csv dialects.
+    This parser uses the databrowser widget to display the data.
 
-    Perhaps this should be rewritten using another CSV lib
-    because the standard module csv does not support unicode.
+    It supports the following parser arguments:
 
-    @copyright: 2004 Oliver Graf <ograf@bitart.de>, Alexander Schremmer
+     * delimiter/separator: the delimiter to use instead of ;
+     * quotechar: quoting character, default off, must be ascii!
+     * show: comma-separated list of columns to show only
+     * hide: comma-separated list of columns to hide
+     * autofilter: comma-separated list of columns to equip with
+                   auto-filter drop down
+     * name: name of the dataset
+     * link: comma separated list of columns that take links, separate
+             the link and the description with a space
+     * static_cols: comma-separated list of columns that are static
+                    and present in each row
+     * static_vals: comma-separated list of values for those static
+                    columns
+
+    The static column feature is only really useful if the dataset
+    postprocessed by some other plugin collecting data from multiple
+    wiki pages.
+
+    @copyright: 2007, 2008 Johannes Berg <johannes@sipsolutions.net>
     @license: GNU GPL, see COPYING for details.
 """
 
-Dependencies = []
+from csv import reader, QUOTE_NONE, QUOTE_MINIMAL
+
+from MoinMoin.util.dataset import TupleDataset, Column
+from MoinMoin.widget.browser import DataBrowserWidget
+from MoinMoin.wikiutil import escape
+
+
+Dependencies = ['time']
 
 class Parser:
-    """ Format CSV data as table
-    """
-
     extensions = ['.csv']
     Dependencies = []
 
-    def __init__(self, raw, request, **kw):
-        """ Store the source text.
-        """
-        self.raw = raw
-        self.request = request
-        self.form = request.form
-        self._ = request.getText
+    def _read_rows(self, r):
+        if self._first_row is not None:
+            yield self._first_row
+        for row in r:
+            yield row
 
-        # parse extra arguments for excludes
-        self.exclude = []
-        self.separator = ';'
-        for arg in kw.get('format_args', '').split():
-            if arg[0] == '-':
-                try:
-                    idx = int(arg[1:])
-                except ValueError:
-                    pass
+    def __init__(self, raw, request, **kw):
+        self.request = request
+        self._first_row = None
+        formatter = request.formatter
+
+        # workaround csv.reader deficiency by encoding to utf-8
+        data = raw.encode('utf-8').split('\n')
+
+        visible = None
+        hiddenindexes = []
+        hiddencols = []
+        autofiltercols = []
+        staticcols = []
+        staticvals = []
+        linkcols = []
+        delimiter = ';'
+        quotechar = '\x00' # can't be entered
+        quoting = QUOTE_NONE
+        name = None
+        hdr = reader([kw.get('format_args', '').strip().encode('utf-8')], delimiter=" ")
+        args = hdr.next()
+
+        for arg in args:
+            arg = arg.decode('utf-8')
+            try:
+                key, val = arg.split('=', 1)
+            except:
+                # handle compatibility with original 'csv' parser
+                if arg.startswith('-'):
+                    try:
+                        hiddenindexes.append(int(arg[1:]) - 1)
+                    except ValueError:
+                        pass
                 else:
-                    self.exclude.append(idx-1)
-            else:
-                self.separator = arg
+                    delimiter = arg.encode('utf-8')
+                continue
+            if key == 'separator' or key == 'delimiter':
+                delimiter = val.encode('utf-8')
+            if key == 'quotechar':
+                if val == val.encode('utf-8'):
+                    quotechar = val.encode('utf-8')
+                    quoting = QUOTE_MINIMAL
+            elif key == 'show':
+                visible = val.split(',')
+            elif key == 'hide':
+                hiddencols = val.split(',')
+            elif key == 'autofilter':
+                autofiltercols = val.split(',')
+            elif key == 'name':
+                name = val
+            elif key == 'static_cols':
+                staticcols = val.split(',')
+            elif key == 'static_vals':
+                staticvals = val.split(',')
+            elif key == 'link':
+                linkcols = val.split(',')
+
+        if len(staticcols) > len(staticvals):
+            staticvals.extend([''] * (len(staticcols)-len(staticvals)))
+        elif len(staticcols) < len(staticvals):
+            staticvals = staticvals[:len(staticcols)]
+
+        r = reader(data, delimiter=delimiter, quotechar=quotechar, quoting=quoting)
+        cols = map(lambda x: x.decode('utf-8'), r.next()) + staticcols
+
+        self._show_header = True
+
+        if cols == staticcols:
+            try:
+                self._first_row = map(lambda x: x.decode('utf-8'), r.next())
+                cols = [None] * len(self._first_row) + staticcols
+                self._show_header = False
+            except StopIteration:
+                pass
+
+        num_entry_cols = len(cols) - len(staticcols)
+
+        if not visible is None:
+            for col in cols:
+                if not col in visible:
+                    hiddencols.append(col)
+
+        linkparse = [False] * len(cols)
+
+        data = TupleDataset(name)
+        for colidx in range(len(cols)):
+            col = cols[colidx]
+            autofilter = col in autofiltercols
+            hidden = col in hiddencols or colidx in hiddenindexes
+            data.columns.append(Column(col, autofilter=autofilter, hidden=hidden))
+
+            linkparse[colidx] = col in linkcols
+
+        for row in self._read_rows(r):
+            row = map(lambda x: x.decode('utf-8'), row)
+            if len(row) > num_entry_cols:
+                row = row[:num_entry_cols]
+            elif len(row) < num_entry_cols:
+                row.extend([''] * (num_entry_cols-len(row)))
+            row += staticvals
+            for colidx in range(len(row)):
+                item = row[colidx]
+                if linkparse[colidx]:
+                    try:
+                        url, item = item.split(' ', 1)
+                        if url == '':
+                            display = escape(item)
+                        else:
+                            display = ''.join([
+                                formatter.url(1, url=url),
+                                formatter.text(item),
+                                formatter.url(0)])
+                    except ValueError:
+                        display = escape(item)
+                else:
+                    display = escape(item)
+                row[colidx] = (display, item)
+            data.addRow(tuple(row))
+        self.data = data
 
     def format(self, formatter):
-        """ Parse and send the table.
-        """
-        lines = self.raw.split('\n')
-        if lines[0]:
-            # expect column headers in first line
-            first = 1
-        else:
-            # empty first line, no bold headers
-            first = 0
-            del lines[0]
-
-        self.request.write(formatter.table(1))
-        for line in lines:
-            self.request.write(formatter.table_row(1))
-            cells = line.split(self.separator)
-            for idx in range(len(cells)):
-                if idx in self.exclude:
-                    continue
-                self.request.write(formatter.table_cell(1))
-                if first:
-                    self.request.write(formatter.strong(1))
-                self.request.write(formatter.text(cells[idx]))
-                if first:
-                    self.request.write(formatter.strong(0))
-                self.request.write(formatter.table_cell(0))
-            self.request.write(formatter.table_row(0))
-            first = 0
-        self.request.write(formatter.table(0))
-
+        browser = DataBrowserWidget(self.request, show_header=self._show_header)
+        browser.setData(self.data)
+        self.request.write(browser.format())
--- a/MoinMoin/parser/text_rst.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/parser/text_rst.py	Wed Mar 19 13:22:25 2008 +0100
@@ -562,17 +562,20 @@
             return
 
         if len(content):
-            page = Page(page_name=content[0], request=self.request)
-            if page.exists():
-                text = page.get_raw_body()
-                lines = text.split('\n')
-                # Remove the "#format rst" line
-                if lines[0].startswith("#format"):
-                    del lines[0]
+            pagename = content[0]
+            page = Page(page_name=pagename, request=self.request)
+            if not self.request.user.may.read(pagename):
+                lines = [_("**You are not allowed to read the page: %s**") % (pagename, )]
             else:
-                lines = [_("**Could not find the referenced page: %s**") % (content[0], )]
-            # Insert the text from the included document and then continue
-            # parsing
+                if page.exists():
+                    text = page.get_raw_body()
+                    lines = text.split('\n')
+                    # Remove the "#format rst" line
+                    if lines[0].startswith("#format"):
+                        del lines[0]
+                else:
+                    lines = [_("**Could not find the referenced page: %s**") % (pagename, )]
+            # Insert the text from the included document and then continue parsing
             state_machine.insert_input(lines, 'MoinDirectives')
         return
 
--- a/MoinMoin/request/__init__.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/request/__init__.py	Wed Mar 19 13:22:25 2008 +0100
@@ -1210,7 +1210,7 @@
 
             # Handle request. We have these options:
             # 1. jump to page where user left off
-            if not pagename and self.user.remember_last_visit:
+            if not pagename and self.user.remember_last_visit and action_name == 'show':
                 pagetrail = self.user.getTrail()
                 if pagetrail:
                     # Redirect to last page visited
--- a/MoinMoin/theme/__init__.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/theme/__init__.py	Wed Mar 19 13:22:25 2008 +0100
@@ -277,7 +277,7 @@
                         request.formatter.interwikilink(0, title=title, id="userhome", *interwiki))
             userlinks.append(homelink)
             # link to userprefs action
-            userlinks.append(d['page'].link_to(request, text=_('Preferences'),
+            userlinks.append(d['page'].link_to(request, text=_('Settings'),
                                                querystr={'action': 'userprefs'}, id='userprefs', rel='nofollow'))
 
         if request.user.valid:
@@ -1010,7 +1010,7 @@
             'pagename_quoted': wikiutil.quoteWikinameURL(self.request.page.page_name)
             }
         html = '''
-<form class="actionsmenu" method="get" action="">
+<form class="actionsmenu" method="POST" action="">
 <div>
     <label>%(label)s</label>
     <select name="action"
@@ -1503,7 +1503,6 @@
         page_title_index = wikiutil.getLocalizedPage(request, 'TitleIndex').page_name
         page_site_navigation = wikiutil.getLocalizedPage(request, 'SiteNavigation').page_name
         page_word_index = wikiutil.getLocalizedPage(request, 'WordIndex').page_name
-        page_user_prefs = wikiutil.getLocalizedPage(request, 'UserPreferences').page_name
         page_help_formatting = wikiutil.getLocalizedPage(request, 'HelpOnFormatting').page_name
         page_find_page = wikiutil.getLocalizedPage(request, 'FindPage').page_name
         home_page = wikiutil.getInterwikiHomePage(request) # sorry theme API change!!! Either None or tuple (wikiname,pagename) now.
@@ -1680,10 +1679,8 @@
                 'page_parent_page': page_parent_page,
                 'page_title_index': page_title_index,
                 'page_word_index': page_word_index,
-                'page_user_prefs': page_user_prefs,
                 'user_name': request.user.name,
                 'user_valid': request.user.valid,
-                'user_prefs': (page_user_prefs, request.user.name)[request.user.valid],
                 'msg': self._status,
                 'trail': keywords.get('trail', None),
                 # Discontinued keys, keep for a while for 3rd party theme developers
--- a/MoinMoin/user.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/user.py	Wed Mar 19 13:22:25 2008 +0100
@@ -6,7 +6,7 @@
     some specific user). User instances are used to access the user profile of
     some specific user (name, password, email, bookmark, trail, settings, ...).
 
-    The UserPreferences form support stuff is in module userform.
+    Some related code is in the userform and userprefs modules.
 
     TODO:
     * code is a mixture of highlevel user stuff and lowlevel storage functions,
@@ -289,7 +289,7 @@
                               default: 'internal'
         @keyword auth_attribs: tuple of user object attribute names that are
                                determined by auth method and should not be
-                               changed by UserPreferences form, default: ().
+                               changeable by preferences, default: ().
                                First tuple element was used for authentication.
         """
         self._cfg = request.cfg
@@ -1044,9 +1044,9 @@
 
 Login Password: %s
 
-Login URL: %s/%s?action=login
+Login URL: %s/?action=login
 """) % (
-                        self.name, self.enc_password, self._request.getBaseURL(), getLocalizedPage(self._request, 'UserPreferences').page_name)
+                        self.name, self.enc_password, self._request.getBaseURL(), )
 
         text = _("""\
 Somebody has requested to submit your account data to this email address.
--- a/MoinMoin/userform/__init__.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/userform/__init__.py	Wed Mar 19 13:22:25 2008 +0100
@@ -1,6 +1,6 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - UserPreferences Form and User Browser
+    MoinMoin - Login form and user browser
 
     @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
                 2003-2007 MoinMoin:ThomasWaldmann
--- a/MoinMoin/userprefs/oid.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/userprefs/oid.py	Wed Mar 19 13:22:25 2008 +0100
@@ -138,6 +138,9 @@
         if form.has_key('cancel'):
             return
 
+        if self.request.request_method != 'POST':
+            return
+
         if form.has_key('remove'):
             return self._handle_remove()
 
--- a/MoinMoin/userprefs/prefs.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/userprefs/prefs.py	Wed Mar 19 13:22:25 2008 +0100
@@ -1,6 +1,6 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - UserPreferences Form and User Browser
+    MoinMoin - Preferences Form
 
     @copyright: 2001-2004 Juergen Hermann <jh@web.de>,
                 2003-2007 MoinMoin:ThomasWaldmann
@@ -14,7 +14,7 @@
 
 
 #################################################################
-# This is a mess.
+# This is still a mess.
 #
 # The plan for refactoring would be:
 # split the plugin into multiple preferences pages:
@@ -61,7 +61,8 @@
         form = self.request.form
 
         if self.request.request_method != 'POST':
-            return _("Use UserPreferences to change your settings or create an account.", wiki=True)
+            return
+
         theuser = self.request.user
         if not theuser:
             return
--- a/MoinMoin/widget/browser.py	Wed Mar 19 13:21:44 2008 +0100
+++ b/MoinMoin/widget/browser.py	Wed Mar 19 13:22:25 2008 +0100
@@ -11,7 +11,7 @@
 
 class DataBrowserWidget(base.Widget):
 
-    def __init__(self, request, **kw):
+    def __init__(self, request, show_header=True, **kw):
         _ = request.getText
         base.Widget.__init__(self, request, **kw)
         self.data = None
@@ -25,6 +25,7 @@
         self.__empty = '[empty]'
         self._filter = _('filter')
         self.__filter = 'filter'
+        self._show_header = show_header
 
     def setData(self, dataset):
         """ Sets the data for the browser (see MoinMoin.util.dataset).
@@ -110,26 +111,27 @@
         result.append(fmt.table(1, id='%stable' % self.data_id))
 
         # add header line
-        result.append(fmt.table_row(1))
-        for idx in range(len(self.data.columns)):
-            col = self.data.columns[idx]
-            if col.hidden:
-                continue
-            result.append(fmt.table_cell(1))
-            result.append(fmt.strong(1))
-            result.append(col.label or col.name)
-            result.append(fmt.strong(0))
+        if self._show_header:
+            result.append(fmt.table_row(1))
+            for idx in range(len(self.data.columns)):
+                col = self.data.columns[idx]
+                if col.hidden:
+                    continue
+                result.append(fmt.table_cell(1))
+                result.append(fmt.strong(1))
+                result.append(col.label or col.name)
+                result.append(fmt.strong(0))
 
-            if col.autofilter:
-                result.append(fmt.linebreak(False))
-                select = '<select %s onchange="dbw_update_search(\'%s\');">%s</select>' % (
-                                  self._name('filter%d' % idx),
-                                  self.data_id,
-                                  self._filteroptions(idx))
-                result.append(fmt.rawHTML(select))
+                if col.autofilter:
+                    result.append(fmt.linebreak(False))
+                    select = '<select %s onchange="dbw_update_search(\'%s\');">%s</select>' % (
+                                      self._name('filter%d' % idx),
+                                      self.data_id,
+                                      self._filteroptions(idx))
+                    result.append(fmt.rawHTML(select))
 
-            result.append(fmt.table_cell(0))
-        result.append(fmt.table_row(0))
+                result.append(fmt.table_cell(0))
+            result.append(fmt.table_row(0))
 
         # add data
         self.data.reset()
--- a/docs/CHANGES	Wed Mar 19 13:21:44 2008 +0100
+++ b/docs/CHANGES	Wed Mar 19 13:22:25 2008 +0100
@@ -103,6 +103,8 @@
   Other changes:
     * cfg.show_login is gone, see code in theme/__init__.py, this affects
       many themes!
+    * The themedict no longer contains 'page_user_prefs' and 'user_prefs',
+      this may affect custom themes.
     * needs a new userprefs/ plugin directory
     * removed attachments direct serving (cfg.attachments)
     * the rst-parser's admonition's class names are no longer prepended with