changeset 1048:b7544e3bd478

Merge with main.
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Mon, 24 Jul 2006 22:18:49 +0200
parents 51086fe55b58 (current diff) 6488692b1eb8 (diff)
children 3b5603bf468d ac85f3b79216
files MoinMoin/config.py MoinMoin/formatter/text_python.py MoinMoin/multiconfig.py MoinMoin/util/diff.py MoinMoin/xmlrpc/__init__.py
diffstat 69 files changed, 2188 insertions(+), 2126 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/_tests/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -94,9 +94,9 @@
         
         Non existing default will raise an AttributeError.
         """
-        from MoinMoin.multiconfig import DefaultConfig
+        from MoinMoin.config import multiconfig
         for key in defaults:
-            self._setattr(key, getattr(DefaultConfig, key))
+            self._setattr(key, getattr(multiconfig.DefaultConfig, key))
 
     def setCustom(self, **custom):
         """ Set custom values """
--- a/MoinMoin/action/AttachFile.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/action/AttachFile.py	Mon Jul 24 22:18:49 2006 +0200
@@ -620,11 +620,12 @@
         # delete map file if it is empty
         os.unlink(savepath)
     else:
-        file = open(savepath, 'wb')
+        stream = open(savepath, 'wb')
         try:
-            file.write(filecontent)
+            stream.write(filecontent)
         finally:
-            file.close()
+            stream.close()
+        os.chmod(savepath, 0666 & config.umask)
 
     # touch attachment directory to invalidate cache if new map is saved
     if ext == '.map':
--- a/MoinMoin/action/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/action/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -303,537 +303,11 @@
     pg.send_page(request, msg=savemsg)
     return None
 
-def do_edit(pagename, request):
-    """ edit a page """
-    _ = request.getText
-
-    if not request.user.may.write(pagename):
-        Page(request, pagename).send_page(request,
-            msg=_('You are not allowed to edit this page.'))
-        return
-
-    valideditors = ['text', 'gui', ]
-    editor = ''
-    if request.user.valid:
-        editor = request.user.editor_default
-    if editor not in valideditors:
-        editor = request.cfg.editor_default
-
-    editorparam = request.form.get('editor', [editor])[0]
-    if editorparam == "guipossible":
-        lasteditor = editor
-    elif editorparam == "textonly":
-        editor = lasteditor = 'text'
-    else:
-        editor = lasteditor = editorparam
-
-    if request.cfg.editor_force:
-        editor = request.cfg.editor_default
-
-    # if it is still nothing valid, we just use the text editor
-    if editor not in valideditors:
-        editor = 'text'
-
-    savetext = request.form.get('savetext', [None])[0]
-    rev = int(request.form.get('rev', ['0'])[0])
-    comment = request.form.get('comment', [u''])[0]
-    category = request.form.get('category', [None])[0]
-    rstrip = int(request.form.get('rstrip', ['0'])[0])
-    trivial = int(request.form.get('trivial', ['0'])[0])
-
-    if request.form.has_key('button_switch'):
-        if editor == 'text':
-            editor = 'gui'
-        else: # 'gui'
-            editor = 'text'
-
-    # load right editor class
-    if editor == 'gui':
-        from MoinMoin.PageGraphicalEditor import PageGraphicalEditor
-        pg = PageGraphicalEditor(request, pagename)
-    else: # 'text'
-        from MoinMoin.PageEditor import PageEditor
-        pg = PageEditor(request, pagename)
-
-    # is invoked without savetext start editing
-    if savetext is None:
-        pg.sendEditor()
-        return
-
-    # did user hit cancel button?
-    cancelled = request.form.has_key('button_cancel')
-
-    # convert input from Graphical editor
-    from MoinMoin.converter.text_html_text_moin_wiki import convert, ConvertError
-    try:
-        if lasteditor == 'gui':
-            savetext = convert(request, pagename, savetext)
-
-        # IMPORTANT: normalize text from the form. This should be done in
-        # one place before we manipulate the text.
-        savetext = pg.normalizeText(savetext, stripspaces=rstrip)
-    except ConvertError:
-        # we don't want to throw an exception if user cancelled anyway
-        if not cancelled:
-            raise
-
-    if cancelled:
-        pg.sendCancel(savetext or "", rev)
-        return
-
-    comment = wikiutil.clean_comment(comment)
-
-    # Add category
-
-    # TODO: this code does not work with extended links, and is doing
-    # things behind your back, and in general not needed. Either we have
-    # a full interface for categories (add, delete) or just add them by
-    # markup.
-
-    if category and category != _('<No addition>', formatted=False): # opera 8.5 needs this
-        # strip trailing whitespace
-        savetext = savetext.rstrip()
-
-        # Add category separator if last non-empty line contains
-        # non-categories.
-        lines = filter(None, savetext.splitlines())
-        if lines:
-
-            #TODO: this code is broken, will not work for extended links
-            #categories, e.g ["category hebrew"]
-            categories = lines[-1].split()
-
-            if categories:
-                confirmed = wikiutil.filterCategoryPages(request, categories)
-                if len(confirmed) < len(categories):
-                    # This was not a categories line, add separator
-                    savetext += u'\n----\n'
-
-        # Add new category
-        if savetext and savetext[-1] != u'\n':
-            savetext += ' '
-        savetext += category + u'\n' # Should end with newline!
-
-    # Preview, spellcheck or spellcheck add new words
-    if (request.form.has_key('button_preview') or
-        request.form.has_key('button_spellcheck') or
-        request.form.has_key('button_newwords')):
-        pg.sendEditor(preview=savetext, comment=comment)
-
-    # Preview with mode switch
-    elif request.form.has_key('button_switch'):
-        pg.sendEditor(preview=savetext, comment=comment, staytop=1)
-
-    # Save new text
-    else:
-        try:
-            still_conflict = wikiutil.containsConflictMarker(savetext)
-            pg.setConflict(still_conflict)
-            savemsg = pg.saveText(savetext, rev, trivial=trivial, comment=comment)
-        except pg.EditConflict, e:
-            msg = e.message
-
-            # Handle conflict and send editor
-            pg.set_raw_body(savetext, modified=1)
-
-            pg.mergeEditConflict(rev)
-            # We don't send preview when we do merge conflict
-            pg.sendEditor(msg=msg, comment=comment)
-            return
-
-        except pg.SaveError, msg:
-            # msg contain a unicode string
-            savemsg = unicode(msg)
-
-        # Send new page after save or after unsuccessful conflict merge.
-        request.reset()
-        backto = request.form.get('backto', [None])[0]
-        if backto:
-            pg = Page(request, backto)
-
-        pg.send_page(request, msg=savemsg)
-
 def do_goto(pagename, request):
     """ redirect to another page """
     target = request.form.get('target', [''])[0]
     request.http_redirect(Page(request, target).url(request))
 
-def do_diff(pagename, request):
-    """ Handle "action=diff"
-        checking for either a "rev=formerrevision" parameter
-        or rev1 and rev2 parameters
-    """
-    if not request.user.may.read(pagename):
-        Page(request, pagename).send_page(request)
-        return
-
-    try:
-        date = request.form['date'][0]
-        try:
-            date = long(date) # must be long for py 2.2.x
-        except StandardError:
-            date = 0
-    except KeyError:
-        date = 0
-
-    try:
-        rev1 = int(request.form.get('rev1', [-1])[0])
-    except StandardError:
-        rev1 = 0
-    try:
-        rev2 = int(request.form.get('rev2', [0])[0])
-    except StandardError:
-        rev1 = 0
-
-    if rev1 == -1 and rev2 == 0:
-        try:
-            rev1 = int(request.form.get('rev', [-1])[0])
-        except StandardError:
-            rev1 = -1
-
-    # spacing flag?
-    ignorews = int(request.form.get('ignorews', [0])[0])
-
-    _ = request.getText
-
-    # get a list of old revisions, and back out if none are available
-    currentpage = Page(request, pagename)
-    revisions = currentpage.getRevList()
-    if len(revisions) < 2:
-        currentpage.send_page(request, msg=_("No older revisions available!"))
-        return
-
-    if date: # this is how we get called from RecentChanges
-        rev1 = 0
-        log = editlog.EditLog(request, rootpagename=pagename)
-        for line in log.reverse():
-            if date >= line.ed_time_usecs and int(line.rev) != 99999999:
-                rev1 = int(line.rev)
-                break
-        else:
-            rev1 = 1
-        rev2 = 0
-
-    # Start output
-    # This action generate content in the user language
-    request.setContentLanguage(request.lang)
-
-    request.http_headers()
-    request.theme.send_title(_('Diff for "%s"') % (pagename,), pagename=pagename, allow_doubleclick=1)
-
-    if rev1 > 0 and rev2 > 0 and rev1 > rev2 or rev1 == 0 and rev2 > 0:
-        rev1, rev2 = rev2, rev1
-
-    oldrev1, oldcount1 = None, 0
-    oldrev2, oldcount2 = None, 0
-
-    # get the filename of the version to compare to
-    edit_count = 0
-    for rev in revisions:
-        edit_count += 1
-        if rev <= rev1:
-            oldrev1, oldcount1 = rev, edit_count
-        if rev2 and rev >= rev2:
-            oldrev2, oldcount2 = rev, edit_count
-        if oldrev1 and oldrev2 or oldrev1 and not rev2:
-            break
-
-    if rev1 == -1:
-        oldpage = Page(request, pagename, rev=revisions[1])
-        oldcount1 -= 1
-    elif rev1 == 0:
-        oldpage = currentpage
-        # oldcount1 is still on init value 0
-    else:
-        if oldrev1:
-            oldpage = Page(request, pagename, rev=oldrev1)
-        else:
-            oldpage = Page(request, "$EmptyPage$") # hack
-            oldpage.set_raw_body("")    # avoid loading from disk
-            oldrev1 = 0 # XXX
-
-    if rev2 == 0:
-        newpage = currentpage
-        # oldcount2 is still on init value 0
-    else:
-        if oldrev2:
-            newpage = Page(request, pagename, rev=oldrev2)
-        else:
-            newpage = Page(request, "$EmptyPage$") # hack
-            newpage.set_raw_body("")    # avoid loading from disk
-            oldrev2 = 0 # XXX
-
-    edit_count = abs(oldcount1 - oldcount2)
-
-    # this should use the formatter, but there is none?
-    request.write('<div id="content">\n') # start content div
-    request.write('<p class="diff-header">')
-    request.write(_('Differences between revisions %d and %d') % (oldpage.get_real_rev(), newpage.get_real_rev()))
-    if edit_count > 1:
-        request.write(' ' + _('(spanning %d versions)') % (edit_count,))
-    request.write('</p>')
-
-    if request.user.show_fancy_diff:
-        from MoinMoin.util.diff import diff
-        request.write(diff(request, oldpage.get_raw_body(), newpage.get_raw_body()))
-        newpage.send_page(request, count_hit=0, content_only=1, content_id="content-below-diff")
-    else:
-        lines = wikiutil.linediff(oldpage.getlines(), newpage.getlines())
-        if not lines:
-            msg = _("No differences found!")
-            if edit_count > 1:
-                msg = msg + '<p>' + _('The page was saved %(count)d times, though!') % {
-                    'count': edit_count}
-            request.write(msg)
-        else:
-            if ignorews:
-                request.write(_('(ignoring whitespace)') + '<br>')
-            else:
-                qstr = 'action=diff&ignorews=1'
-                if rev1: qstr = '%s&rev1=%s' % (qstr, rev1)
-                if rev2: qstr = '%s&rev2=%s' % (qstr, rev2)
-                request.write(Page(request, pagename).link_to(request,
-                    text=_('Ignore changes in the amount of whitespace'),
-                    querystr=qstr, rel='nofollow') + '<p>')
-
-            request.write('<pre>')
-            for line in lines:
-                if line[0] == "@":
-                    request.write('<hr>')
-                request.write(wikiutil.escape(line)+'\n')
-            request.write('</pre>')
-
-    request.write('</div>\n') # end content div
-    request.theme.send_footer(pagename)
-    request.theme.send_closing_html()
-
-def do_info(pagename, request):
-    """ show misc. infos about a page """
-    if not request.user.may.read(pagename):
-        Page(request, pagename).send_page(request)
-        return
-
-    def general(page, pagename, request):
-        _ = request.getText
-
-        request.write('<h2>%s</h2>\n' % _('General Information'))
-
-        # show page size
-        request.write(("<p>%s</p>" % _("Page size: %d")) % page.size())
-
-        # show SHA digest fingerprint
-        import sha
-        digest = sha.new(page.get_raw_body().encode(config.charset)).hexdigest().upper()
-        request.write('<p>%(label)s <tt>%(value)s</tt></p>' % {
-            'label': _("SHA digest of this page's content is:"),
-            'value': digest,
-            })
-
-        # show attachments (if allowed)
-        attachment_info = getHandler(request, 'AttachFile', 'info')
-        if attachment_info:
-            request.write(attachment_info(pagename, request))
-
-        # show subscribers
-        subscribers = page.getSubscribers(request, include_self=1, return_users=1)
-        if subscribers:
-            request.write('<p>', _('The following users subscribed to this page:'))
-            for lang in subscribers.keys():
-                request.write('<br>[%s] ' % lang)
-                for user in subscribers[lang]:
-                    # do NOT disclose email addr, only WikiName
-                    userhomepage = Page(request, user.name)
-                    if userhomepage.exists():
-                        request.write(userhomepage.link_to(request) + ' ')
-                    else:
-                        request.write(user.name + ' ')
-            request.write('</p>')
-
-        # show links
-        links = page.getPageLinks(request)
-        if links:
-            request.write('<p>', _('This page links to the following pages:'), '<br>')
-            for linkedpage in links:
-                request.write("%s%s " % (Page(request, linkedpage).link_to(request), ",."[linkedpage == links[-1]]))
-            request.write("</p>")
-
-    def history(page, pagename, request):
-        # show history as default
-        _ = request.getText
-
-        # open log for this page
-        from MoinMoin.util.dataset import TupleDataset, Column
-
-        history = TupleDataset()
-        history.columns = [
-            Column('rev', label='#', align='right'),
-            Column('mtime', label=_('Date'), align='right'),
-            Column('size', label=_('Size'), align='right'),
-            Column('diff', label='<input type="submit" value="%s">' % (_("Diff"))),
-            Column('editor', label=_('Editor'), hidden=not request.cfg.show_names),
-            Column('comment', label=_('Comment')),
-            Column('action', label=_('Action')),
-            ]
-
-        # generate history list
-        revisions = page.getRevList()
-        versions = len(revisions)
-
-        may_revert = request.user.may.revert(pagename)
-
-        # read in the complete log of this page
-        log = editlog.EditLog(request, rootpagename=pagename)
-        count = 0
-        for line in log.reverse():
-            rev = int(line.rev)
-            actions = ""
-            if line.action in ['SAVE', 'SAVENEW', 'SAVE/REVERT', ]:
-                size = page.size(rev=rev)
-                if count == 0: # latest page
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('view'),
-                        querystr=''))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('raw'),
-                        querystr='action=raw', rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('print'),
-                        querystr='action=print', rel='nofollow'))
-                else:
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('view'),
-                        querystr='action=recall&rev=%d' % rev, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('raw'),
-                        querystr='action=raw&rev=%d' % rev, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('print'),
-                        querystr='action=print&rev=%d' % rev, rel='nofollow'))
-                    if may_revert and size: # you can only revert to nonempty revisions
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('revert'),
-                            querystr='action=revert&rev=%d' % rev, rel='nofollow'))
-                if count == 0:
-                    rchecked = ' checked="checked"'
-                    lchecked = ''
-                elif count == 1:
-                    lchecked = ' checked="checked"'
-                    rchecked = ''
-                else:
-                    lchecked = rchecked = ''
-                diff = '<input type="radio" name="rev1" value="%d"%s><input type="radio" name="rev2" value="%d"%s>' % (rev, lchecked, rev, rchecked)
-                comment = line.comment
-                if not comment and '/REVERT' in line.action:
-                        comment = _("Revert to revision %(rev)d.") % {'rev': int(line.extra)}
-            else: # ATT*
-                rev = '-'
-                diff = '-'
-
-                filename = wikiutil.url_unquote(line.extra)
-                comment = "%s: %s %s" % (line.action, filename, line.comment)
-                size = 0
-                if line.action != 'ATTDEL':
-                    from MoinMoin.action import AttachFile
-                    page_dir = AttachFile.getAttachDir(request, pagename)
-                    filepath = os.path.join(page_dir, filename)
-                    try:
-                        # FIXME, wrong path on non-std names
-                        size = os.path.getsize(filepath)
-                    except:
-                        pass
-                    if line.action == 'ATTNEW':
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('view'),
-                            querystr='action=AttachFile&do=view&target=%s' % filename, rel='nofollow'))
-                    elif line.action == 'ATTDRW':
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('edit'),
-                            querystr='action=AttachFile&drawing=%s' % filename.replace(".draw", ""), rel='nofollow'))
-
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('get'),
-                        querystr='action=AttachFile&do=get&target=%s' % filename, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('del'),
-                        querystr='action=AttachFile&do=del&target=%s' % filename, rel='nofollow'))
-                    # XXX use?: wikiutil.escape(filename)
-
-            history.addRow((
-                rev,
-                request.user.getFormattedDateTime(wikiutil.version2timestamp(line.ed_time_usecs)),
-                str(size),
-                diff,
-                line.getEditor(request) or _("N/A"),
-                wikiutil.escape(comment) or '&nbsp;',
-                actions,
-            ))
-            count += 1
-            if count >= 100:
-                break
-
-        # print version history
-        from MoinMoin.widget.browser import DataBrowserWidget
-
-        request.write('<h2>%s</h2>\n' % _('Revision History'))
-
-        if not count: # there was no entry in logfile
-            request.write(_('No log entries found.'))
-            return
-
-        # TODO: this form activates revert, which should use POST, but
-        # other actions should use get. Maybe we should put the revert
-        # into the page view itself, and not in this form.
-        request.write('<form method="GET" action="">\n')
-        request.write('<div id="page-history">\n')
-        request.write('<input type="hidden" name="action" value="diff">\n')
-
-        history_table = DataBrowserWidget(request)
-        history_table.setData(history)
-        history_table.render()
-        request.write('</div>\n')
-        request.write('</form>\n')
-
-    # main function
-    _ = request.getText
-    page = Page(request, pagename)
-    qpagename = wikiutil.quoteWikinameURL(pagename)
-    title = page.split_title(request)
-
-    request.http_headers()
-
-    # This action uses page or wiki language TODO: currently
-    # page.language is broken and not available now, when we fix it,
-    # this will be automatically fixed.
-    lang = page.language or request.cfg.language_default
-    request.setContentLanguage(lang)
-
-    request.theme.send_title(_('Info for "%s"') % (title,), pagename=pagename)
-
-    historylink = wikiutil.link_tag(request, '%s?action=info' % qpagename,
-        _('Show "%(title)s"') % {'title': _('Revision History')}, request.formatter, rel='nofollow')
-    generallink = wikiutil.link_tag(request, '%s?action=info&amp;general=1' % qpagename,
-        _('Show "%(title)s"') % {'title': _('General Page Infos')}, request.formatter, rel='nofollow')
-    hitcountlink = wikiutil.link_tag(request, '%s?action=info&amp;hitcounts=1' % qpagename,
-        _('Show chart "%(title)s"') % {'title': _('Page hits and edits')}, request.formatter, rel='nofollow')
-
-    request.write('<div id="content">\n') # start content div
-    request.write("<p>[%s]  [%s]  [%s]</p>" % (historylink, generallink, hitcountlink))
-
-    show_hitcounts = int(request.form.get('hitcounts', [0])[0]) != 0
-    show_general = int(request.form.get('general', [0])[0]) != 0
-
-    if show_hitcounts:
-        from MoinMoin.stats import hitcounts
-        request.write(hitcounts.linkto(pagename, request, 'page=' + wikiutil.url_quote_plus(pagename)))
-    elif show_general:
-        general(page, pagename, request)
-    else:
-        history(page, pagename, request)
-
-    request.write('</div>\n') # end content div
-    request.theme.send_footer(pagename)
-    request.theme.send_closing_html()
-
 def do_quicklink(pagename, request):
     """ Add the current wiki page to the user quicklinks 
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/diff.py	Mon Jul 24 22:18:49 2006 +0200
@@ -0,0 +1,163 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - show diff between 2 page revisions
+
+    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
+                2006 by MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import wikiutil
+from MoinMoin.logfile import editlog
+from MoinMoin.Page import Page
+
+def execute(pagename, request):
+    """ Handle "action=diff"
+        checking for either a "rev=formerrevision" parameter
+        or rev1 and rev2 parameters
+    """
+    if not request.user.may.read(pagename):
+        Page(request, pagename).send_page(request)
+        return
+
+    try:
+        date = request.form['date'][0]
+        try:
+            date = long(date) # must be long for py 2.2.x
+        except StandardError:
+            date = 0
+    except KeyError:
+        date = 0
+
+    try:
+        rev1 = int(request.form.get('rev1', [-1])[0])
+    except StandardError:
+        rev1 = 0
+    try:
+        rev2 = int(request.form.get('rev2', [0])[0])
+    except StandardError:
+        rev1 = 0
+
+    if rev1 == -1 and rev2 == 0:
+        try:
+            rev1 = int(request.form.get('rev', [-1])[0])
+        except StandardError:
+            rev1 = -1
+
+    # spacing flag?
+    ignorews = int(request.form.get('ignorews', [0])[0])
+
+    _ = request.getText
+
+    # get a list of old revisions, and back out if none are available
+    currentpage = Page(request, pagename)
+    revisions = currentpage.getRevList()
+    if len(revisions) < 2:
+        currentpage.send_page(request, msg=_("No older revisions available!"))
+        return
+
+    if date: # this is how we get called from RecentChanges
+        rev1 = 0
+        log = editlog.EditLog(request, rootpagename=pagename)
+        for line in log.reverse():
+            if date >= line.ed_time_usecs and int(line.rev) != 99999999:
+                rev1 = int(line.rev)
+                break
+        else:
+            rev1 = 1
+        rev2 = 0
+
+    # Start output
+    # This action generate content in the user language
+    request.setContentLanguage(request.lang)
+
+    request.http_headers()
+    request.theme.send_title(_('Diff for "%s"') % (pagename,), pagename=pagename, allow_doubleclick=1)
+
+    if rev1 > 0 and rev2 > 0 and rev1 > rev2 or rev1 == 0 and rev2 > 0:
+        rev1, rev2 = rev2, rev1
+
+    oldrev1, oldcount1 = None, 0
+    oldrev2, oldcount2 = None, 0
+
+    # get the filename of the version to compare to
+    edit_count = 0
+    for rev in revisions:
+        edit_count += 1
+        if rev <= rev1:
+            oldrev1, oldcount1 = rev, edit_count
+        if rev2 and rev >= rev2:
+            oldrev2, oldcount2 = rev, edit_count
+        if oldrev1 and oldrev2 or oldrev1 and not rev2:
+            break
+
+    if rev1 == -1:
+        oldpage = Page(request, pagename, rev=revisions[1])
+        oldcount1 -= 1
+    elif rev1 == 0:
+        oldpage = currentpage
+        # oldcount1 is still on init value 0
+    else:
+        if oldrev1:
+            oldpage = Page(request, pagename, rev=oldrev1)
+        else:
+            oldpage = Page(request, "$EmptyPage$") # hack
+            oldpage.set_raw_body("")    # avoid loading from disk
+            oldrev1 = 0 # XXX
+
+    if rev2 == 0:
+        newpage = currentpage
+        # oldcount2 is still on init value 0
+    else:
+        if oldrev2:
+            newpage = Page(request, pagename, rev=oldrev2)
+        else:
+            newpage = Page(request, "$EmptyPage$") # hack
+            newpage.set_raw_body("")    # avoid loading from disk
+            oldrev2 = 0 # XXX
+
+    edit_count = abs(oldcount1 - oldcount2)
+
+    # this should use the formatter, but there is none?
+    request.write('<div id="content">\n') # start content div
+    request.write('<p class="diff-header">')
+    request.write(_('Differences between revisions %d and %d') % (oldpage.get_real_rev(), newpage.get_real_rev()))
+    if edit_count > 1:
+        request.write(' ' + _('(spanning %d versions)') % (edit_count,))
+    request.write('</p>')
+
+    if request.user.show_fancy_diff:
+        from MoinMoin.util import diff_html
+        request.write(diff_html.diff(request, oldpage.get_raw_body(), newpage.get_raw_body()))
+        newpage.send_page(request, count_hit=0, content_only=1, content_id="content-below-diff")
+    else:
+        from MoinMoin.util import diff_text
+        lines = diff_text.diff(oldpage.getlines(), newpage.getlines())
+        if not lines:
+            msg = _("No differences found!")
+            if edit_count > 1:
+                msg = msg + '<p>' + _('The page was saved %(count)d times, though!') % {
+                    'count': edit_count}
+            request.write(msg)
+        else:
+            if ignorews:
+                request.write(_('(ignoring whitespace)') + '<br>')
+            else:
+                qstr = 'action=diff&ignorews=1'
+                if rev1: qstr = '%s&rev1=%s' % (qstr, rev1)
+                if rev2: qstr = '%s&rev2=%s' % (qstr, rev2)
+                request.write(Page(request, pagename).link_to(request,
+                    text=_('Ignore changes in the amount of whitespace'),
+                    querystr=qstr, rel='nofollow') + '<p>')
+
+            request.write('<pre>')
+            for line in lines:
+                if line[0] == "@":
+                    request.write('<hr>')
+                request.write(wikiutil.escape(line)+'\n')
+            request.write('</pre>')
+
+    request.write('</div>\n') # end content div
+    request.theme.send_footer(pagename)
+    request.theme.send_closing_html()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/edit.py	Mon Jul 24 22:18:49 2006 +0200
@@ -0,0 +1,164 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - edit a page
+
+    This either calls the text or the GUI page editor.
+
+    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
+                2006 by MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin import wikiutil
+from MoinMoin.Page import Page
+
+def execute(pagename, request):
+    """ edit a page """
+    _ = request.getText
+
+    if not request.user.may.write(pagename):
+        Page(request, pagename).send_page(request,
+            msg=_('You are not allowed to edit this page.'))
+        return
+
+    valideditors = ['text', 'gui', ]
+    editor = ''
+    if request.user.valid:
+        editor = request.user.editor_default
+    if editor not in valideditors:
+        editor = request.cfg.editor_default
+
+    editorparam = request.form.get('editor', [editor])[0]
+    if editorparam == "guipossible":
+        lasteditor = editor
+    elif editorparam == "textonly":
+        editor = lasteditor = 'text'
+    else:
+        editor = lasteditor = editorparam
+
+    if request.cfg.editor_force:
+        editor = request.cfg.editor_default
+
+    # if it is still nothing valid, we just use the text editor
+    if editor not in valideditors:
+        editor = 'text'
+
+    savetext = request.form.get('savetext', [None])[0]
+    rev = int(request.form.get('rev', ['0'])[0])
+    comment = request.form.get('comment', [u''])[0]
+    category = request.form.get('category', [None])[0]
+    rstrip = int(request.form.get('rstrip', ['0'])[0])
+    trivial = int(request.form.get('trivial', ['0'])[0])
+
+    if request.form.has_key('button_switch'):
+        if editor == 'text':
+            editor = 'gui'
+        else: # 'gui'
+            editor = 'text'
+
+    # load right editor class
+    if editor == 'gui':
+        from MoinMoin.PageGraphicalEditor import PageGraphicalEditor
+        pg = PageGraphicalEditor(request, pagename)
+    else: # 'text'
+        from MoinMoin.PageEditor import PageEditor
+        pg = PageEditor(request, pagename)
+
+    # is invoked without savetext start editing
+    if savetext is None:
+        pg.sendEditor()
+        return
+
+    # did user hit cancel button?
+    cancelled = request.form.has_key('button_cancel')
+
+    # convert input from Graphical editor
+    from MoinMoin.converter.text_html_text_moin_wiki import convert, ConvertError
+    try:
+        if lasteditor == 'gui':
+            savetext = convert(request, pagename, savetext)
+
+        # IMPORTANT: normalize text from the form. This should be done in
+        # one place before we manipulate the text.
+        savetext = pg.normalizeText(savetext, stripspaces=rstrip)
+    except ConvertError:
+        # we don't want to throw an exception if user cancelled anyway
+        if not cancelled:
+            raise
+
+    if cancelled:
+        pg.sendCancel(savetext or "", rev)
+        return
+
+    comment = wikiutil.clean_comment(comment)
+
+    # Add category
+
+    # TODO: this code does not work with extended links, and is doing
+    # things behind your back, and in general not needed. Either we have
+    # a full interface for categories (add, delete) or just add them by
+    # markup.
+
+    if category and category != _('<No addition>', formatted=False): # opera 8.5 needs this
+        # strip trailing whitespace
+        savetext = savetext.rstrip()
+
+        # Add category separator if last non-empty line contains
+        # non-categories.
+        lines = filter(None, savetext.splitlines())
+        if lines:
+
+            #TODO: this code is broken, will not work for extended links
+            #categories, e.g ["category hebrew"]
+            categories = lines[-1].split()
+
+            if categories:
+                confirmed = wikiutil.filterCategoryPages(request, categories)
+                if len(confirmed) < len(categories):
+                    # This was not a categories line, add separator
+                    savetext += u'\n----\n'
+
+        # Add new category
+        if savetext and savetext[-1] != u'\n':
+            savetext += ' '
+        savetext += category + u'\n' # Should end with newline!
+
+    # Preview, spellcheck or spellcheck add new words
+    if (request.form.has_key('button_preview') or
+        request.form.has_key('button_spellcheck') or
+        request.form.has_key('button_newwords')):
+        pg.sendEditor(preview=savetext, comment=comment)
+
+    # Preview with mode switch
+    elif request.form.has_key('button_switch'):
+        pg.sendEditor(preview=savetext, comment=comment, staytop=1)
+
+    # Save new text
+    else:
+        try:
+            still_conflict = wikiutil.containsConflictMarker(savetext)
+            pg.setConflict(still_conflict)
+            request.http_headers() # XXX WHY? XXX
+            savemsg = pg.saveText(savetext, rev, trivial=trivial, comment=comment)
+        except pg.EditConflict, e:
+            msg = e.message
+
+            # Handle conflict and send editor
+            pg.set_raw_body(savetext, modified=1)
+
+            pg.mergeEditConflict(rev)
+            # We don't send preview when we do merge conflict
+            pg.sendEditor(msg=msg, comment=comment)
+            return
+
+        except pg.SaveError, msg:
+            # msg contains a unicode string
+            savemsg = unicode(msg)
+
+        # Send new page after save or after unsuccessful conflict merge.
+        request.reset()
+        backto = request.form.get('backto', [None])[0]
+        if backto:
+            pg = Page(request, backto)
+
+        pg.send_page(request, msg=savemsg)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/info.py	Mon Jul 24 22:18:49 2006 +0200
@@ -0,0 +1,245 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - info action
+
+    Displays page history, some general page infos and statistics.
+
+    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
+                2006 by MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import config, wikiutil, action
+from MoinMoin.Page import Page
+from MoinMoin.logfile import editlog
+
+def execute(pagename, request):
+    """ show misc. infos about a page """
+    if not request.user.may.read(pagename):
+        Page(request, pagename).send_page(request)
+        return
+
+    def general(page, pagename, request):
+        _ = request.getText
+
+        request.write('<h2>%s</h2>\n' % _('General Information'))
+
+        # show page size
+        request.write(("<p>%s</p>" % _("Page size: %d")) % page.size())
+
+        # show SHA digest fingerprint
+        import sha
+        digest = sha.new(page.get_raw_body().encode(config.charset)).hexdigest().upper()
+        request.write('<p>%(label)s <tt>%(value)s</tt></p>' % {
+            'label': _("SHA digest of this page's content is:"),
+            'value': digest,
+            })
+
+        # show attachments (if allowed)
+        attachment_info = action.getHandler(request, 'AttachFile', 'info')
+        if attachment_info:
+            request.write(attachment_info(pagename, request))
+
+        # show subscribers
+        subscribers = page.getSubscribers(request, include_self=1, return_users=1)
+        if subscribers:
+            request.write('<p>', _('The following users subscribed to this page:'))
+            for lang in subscribers.keys():
+                request.write('<br>[%s] ' % lang)
+                for user in subscribers[lang]:
+                    # do NOT disclose email addr, only WikiName
+                    userhomepage = Page(request, user.name)
+                    if userhomepage.exists():
+                        request.write(userhomepage.link_to(request) + ' ')
+                    else:
+                        request.write(user.name + ' ')
+            request.write('</p>')
+
+        # show links
+        links = page.getPageLinks(request)
+        if links:
+            request.write('<p>', _('This page links to the following pages:'), '<br>')
+            for linkedpage in links:
+                request.write("%s%s " % (Page(request, linkedpage).link_to(request), ",."[linkedpage == links[-1]]))
+            request.write("</p>")
+
+    def history(page, pagename, request):
+        # show history as default
+        _ = request.getText
+
+        # open log for this page
+        from MoinMoin.util.dataset import TupleDataset, Column
+
+        history = TupleDataset()
+        history.columns = [
+            Column('rev', label='#', align='right'),
+            Column('mtime', label=_('Date'), align='right'),
+            Column('size', label=_('Size'), align='right'),
+            Column('diff', label='<input type="submit" value="%s">' % (_("Diff"))),
+            Column('editor', label=_('Editor'), hidden=not request.cfg.show_names),
+            Column('comment', label=_('Comment')),
+            Column('action', label=_('Action')),
+            ]
+
+        # generate history list
+        revisions = page.getRevList()
+        versions = len(revisions)
+
+        may_revert = request.user.may.revert(pagename)
+
+        # read in the complete log of this page
+        log = editlog.EditLog(request, rootpagename=pagename)
+        count = 0
+        for line in log.reverse():
+            rev = int(line.rev)
+            actions = ""
+            if line.action in ['SAVE', 'SAVENEW', 'SAVE/REVERT', ]:
+                size = page.size(rev=rev)
+                if count == 0: # latest page
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('view'),
+                        querystr=''))
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('raw'),
+                        querystr='action=raw', rel='nofollow'))
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('print'),
+                        querystr='action=print', rel='nofollow'))
+                else:
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('view'),
+                        querystr='action=recall&rev=%d' % rev, rel='nofollow'))
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('raw'),
+                        querystr='action=raw&rev=%d' % rev, rel='nofollow'))
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('print'),
+                        querystr='action=print&rev=%d' % rev, rel='nofollow'))
+                    if may_revert and size: # you can only revert to nonempty revisions
+                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                            text=_('revert'),
+                            querystr='action=revert&rev=%d' % rev, rel='nofollow'))
+                if count == 0:
+                    rchecked = ' checked="checked"'
+                    lchecked = ''
+                elif count == 1:
+                    lchecked = ' checked="checked"'
+                    rchecked = ''
+                else:
+                    lchecked = rchecked = ''
+                diff = '<input type="radio" name="rev1" value="%d"%s><input type="radio" name="rev2" value="%d"%s>' % (rev, lchecked, rev, rchecked)
+                comment = line.comment
+                if not comment and '/REVERT' in line.action:
+                        comment = _("Revert to revision %(rev)d.") % {'rev': int(line.extra)}
+            else: # ATT*
+                rev = '-'
+                diff = '-'
+
+                filename = wikiutil.url_unquote(line.extra)
+                comment = "%s: %s %s" % (line.action, filename, line.comment)
+                size = 0
+                if line.action != 'ATTDEL':
+                    from MoinMoin.action import AttachFile
+                    page_dir = AttachFile.getAttachDir(request, pagename)
+                    filepath = os.path.join(page_dir, filename)
+                    try:
+                        # FIXME, wrong path on non-std names
+                        size = os.path.getsize(filepath)
+                    except:
+                        pass
+                    if line.action == 'ATTNEW':
+                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                            text=_('view'),
+                            querystr='action=AttachFile&do=view&target=%s' % filename, rel='nofollow'))
+                    elif line.action == 'ATTDRW':
+                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                            text=_('edit'),
+                            querystr='action=AttachFile&drawing=%s' % filename.replace(".draw", ""), rel='nofollow'))
+
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('get'),
+                        querystr='action=AttachFile&do=get&target=%s' % filename, rel='nofollow'))
+                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
+                        text=_('del'),
+                        querystr='action=AttachFile&do=del&target=%s' % filename, rel='nofollow'))
+                    # XXX use?: wikiutil.escape(filename)
+
+            history.addRow((
+                rev,
+                request.user.getFormattedDateTime(wikiutil.version2timestamp(line.ed_time_usecs)),
+                str(size),
+                diff,
+                line.getEditor(request) or _("N/A"),
+                wikiutil.escape(comment) or '&nbsp;',
+                actions,
+            ))
+            count += 1
+            if count >= 100:
+                break
+
+        # print version history
+        from MoinMoin.widget.browser import DataBrowserWidget
+
+        request.write('<h2>%s</h2>\n' % _('Revision History'))
+
+        if not count: # there was no entry in logfile
+            request.write(_('No log entries found.'))
+            return
+
+        # TODO: this form activates revert, which should use POST, but
+        # other actions should use get. Maybe we should put the revert
+        # into the page view itself, and not in this form.
+        request.write('<form method="GET" action="">\n')
+        request.write('<div id="page-history">\n')
+        request.write('<input type="hidden" name="action" value="diff">\n')
+
+        history_table = DataBrowserWidget(request)
+        history_table.setData(history)
+        history_table.render()
+        request.write('</div>\n')
+        request.write('</form>\n')
+
+    # main function
+    _ = request.getText
+    page = Page(request, pagename)
+    qpagename = wikiutil.quoteWikinameURL(pagename)
+    title = page.split_title(request)
+
+    request.http_headers()
+
+    # This action uses page or wiki language TODO: currently
+    # page.language is broken and not available now, when we fix it,
+    # this will be automatically fixed.
+    lang = page.language or request.cfg.language_default
+    request.setContentLanguage(lang)
+
+    request.theme.send_title(_('Info for "%s"') % (title,), pagename=pagename)
+    menu_items = [
+        (_('Show "%(title)s"') % {'title': _('Revision History')},
+         {'action': 'info'}),
+        (_('Show "%(title)s"') % {'title': _('General Page Infos')},
+         {'action': 'info', 'general': '1'}),
+        (_('Show "%(title)s"') % {'title': _('Page hits and edits')},
+         {'action': 'info', 'hitcounts': '1'}),
+    ]
+    request.write('<div id="content">\n') # start content div
+    request.write("<p>")
+    for text, querystr in menu_items:
+        request.write("[%s] " % page.link_to(request, text=text, querystr=querystr, rel='nofollow'))
+    request.write("</p>")
+
+    show_hitcounts = int(request.form.get('hitcounts', [0])[0]) != 0
+    show_general = int(request.form.get('general', [0])[0]) != 0
+
+    if show_hitcounts:
+        from MoinMoin.stats import hitcounts
+        request.write(hitcounts.linkto(pagename, request, 'page=' + wikiutil.url_quote_plus(pagename)))
+    elif show_general:
+        general(page, pagename, request)
+    else:
+        history(page, pagename, request)
+
+    request.write('</div>\n') # end content div
+    request.theme.send_footer(pagename)
+    request.theme.send_closing_html()
+
--- a/MoinMoin/config.py	Sat Jul 22 13:38:15 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - site-wide configuration defaults (NOT per single wiki!)
-
-    @copyright: 2005-2006 by Thomas Waldmann (MoinMoin:ThomasWaldmann)
-    @license: GNU GPL, see COPYING for details.
-"""
-import re
-
-# Threads flag - if you write a moin server that use threads, import
-# config in the server and set this flag to True.
-use_threads = False
-
-# Charset - we support only 'utf-8'. While older encodings might work,
-# we don't have the resources to test them, and there is no real
-# benefit for the user. IMPORTANT: use only lowercase 'utf-8'!
-charset = 'utf-8'
-
-# Invalid characters - invisible characters that should not be in page
-# names. Prevent user confusion and wiki abuse, e.g u'\u202aFrontPage'.
-page_invalid_chars_regex = re.compile(
-    ur"""
-    \u0000 | # NULL
-
-    # Bidi control characters
-    \u202A | # LRE
-    \u202B | # RLE
-    \u202C | # PDF
-    \u202D | # LRM
-    \u202E   # RLM
-    """,
-    re.UNICODE | re.VERBOSE
-    )
-
-# Other stuff
-umask = 0770
-url_schemas = []
-
-smileys = (r"X-( :D <:( :o :( :) B) :)) ;) /!\ <!> (!) :-? :\ >:> |) " +
-           r":-( :-) B-) :-)) ;-) |-) (./) {OK} {X} {i} {1} {2} {3} {*} {o}").split()
-
-# unicode: set the char types (upper, lower, digits, spaces)
-from MoinMoin.util.chartypes import _chartypes
-for key, val in _chartypes.items():
-    if not vars().has_key(key):
-        vars()[key] = val
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/config/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -0,0 +1,50 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - site-wide configuration defaults (NOT per single wiki!)
+
+    @copyright: 2005-2006 by Thomas Waldmann (MoinMoin:ThomasWaldmann)
+    @license: GNU GPL, see COPYING for details.
+"""
+import re
+
+# Threads flag - if you write a moin server that use threads, import
+# config in the server and set this flag to True.
+use_threads = False
+
+# Charset - we support only 'utf-8'. While older encodings might work,
+# we don't have the resources to test them, and there is no real
+# benefit for the user. IMPORTANT: use only lowercase 'utf-8'!
+charset = 'utf-8'
+
+# When creating files, we use e.g. 0666 & config.umask for the mode:
+umask = 0770
+
+# Invalid characters - invisible characters that should not be in page
+# names. Prevent user confusion and wiki abuse, e.g u'\u202aFrontPage'.
+page_invalid_chars_regex = re.compile(
+    ur"""
+    \u0000 | # NULL
+
+    # Bidi control characters
+    \u202A | # LRE
+    \u202B | # RLE
+    \u202C | # PDF
+    \u202D | # LRM
+    \u202E   # RLM
+    """,
+    re.UNICODE | re.VERBOSE
+    )
+
+# Other stuff
+url_schemas = []
+
+smileys = (r"X-( :D <:( :o :( :) B) :)) ;) /!\ <!> (!) :-? :\ >:> |) " +
+           r":-( :-) B-) :-)) ;-) |-) (./) {OK} {X} {i} {1} {2} {3} {*} {o}").split()
+
+# unicode: set the char types (upper, lower, digits, spaces)
+from MoinMoin.util.chartypes import _chartypes
+for key, val in _chartypes.items():
+    if not vars().has_key(key):
+        vars()[key] = val
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/config/multiconfig.py	Mon Jul 24 22:18:49 2006 +0200
@@ -0,0 +1,716 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Multiple configuration handler and Configuration defaults class
+
+    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
+                2005-2006 by MoinMoin:ThomasWaldmann.
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import re, os, sys
+from MoinMoin import error
+import MoinMoin.auth as authmodule
+
+_url_re_cache = None
+_farmconfig_mtime = None
+_config_cache = {}
+
+
+def _importConfigModule(name):
+    """ Import and return configuration module and its modification time
+    
+    Handle all errors except ImportError, because missing file is not
+    always an error.
+    
+    @param name: module name
+    @rtype: tuple
+    @return: module, modification time
+    """
+    try:
+        module = __import__(name, globals(), {})
+        mtime = os.path.getmtime(module.__file__)
+    except ImportError:
+        raise
+    except IndentationError, err:
+        msg = '''IndentationError: %(err)s
+
+The configuration files are python modules. Therefore, whitespace is
+important. Make sure that you use only spaces, no tabs are allowed here!
+You have to use four spaces at the beginning of the line mostly.
+''' % {
+    'err': err,
+}
+        raise error.ConfigurationError(msg)
+    except Exception, err:
+        msg = '%s: %s' % (err.__class__.__name__, str(err))
+        raise error.ConfigurationError(msg)
+    return module, mtime
+
+
+def _url_re_list():
+    """ Return url matching regular expression
+
+    Import wikis list from farmconfig on the first call and compile the
+    regexes. Later then return the cached regex list.
+
+    @rtype: list of tuples of (name, compiled re object)
+    @return: url to wiki config name matching list
+    """
+    global _url_re_cache, _farmconfig_mtime
+    if _url_re_cache is None:
+        try:
+            farmconfig, _farmconfig_mtime = _importConfigModule('farmconfig')
+        except ImportError:
+            # Default to wikiconfig for all urls.
+            _farmconfig_mtime = 0
+            _url_re_cache = [('wikiconfig', re.compile(r'.')), ] # matches everything
+        else:
+            try:
+                cache = []
+                for name, regex in farmconfig.wikis:
+                    cache.append((name, re.compile(regex)))
+                _url_re_cache = cache
+            except AttributeError:
+                msg = """
+Missing required 'wikis' list in 'farmconfig.py'.
+
+If you run a single wiki you do not need farmconfig.py. Delete it and
+use wikiconfig.py.
+"""
+                raise error.ConfigurationError(msg)
+    return _url_re_cache
+
+
+def _makeConfig(name):
+    """ Create and return a config instance 
+
+    Timestamp config with either module mtime or farmconfig mtime. This
+    mtime can be used later to invalidate older caches.
+
+    @param name: module name
+    @rtype: DefaultConfig sub class instance
+    @return: new configuration instance
+    """
+    global _farmconfig_mtime
+    try:
+        module, mtime = _importConfigModule(name)
+        configClass = getattr(module, 'Config')
+        cfg = configClass(name)
+        cfg.cfg_mtime = max(mtime, _farmconfig_mtime)
+    except ImportError, err:
+        msg = '''ImportError: %(err)s
+
+Check that the file is in the same directory as the server script. If
+it is not, you must add the path of the directory where the file is
+located to the python path in the server script. See the comments at
+the top of the server script.
+
+Check that the configuration file name is either "wikiconfig.py" or the
+module name specified in the wikis list in farmconfig.py. Note that the
+module name does not include the ".py" suffix.
+''' % {
+    'err': err,
+}
+        raise error.ConfigurationError(msg)
+    except AttributeError, err:
+        msg = '''AttributeError: %(err)s
+
+Could not find required "Config" class in "%(name)s.py".
+
+This might happen if you are trying to use a pre 1.3 configuration file, or
+made a syntax or spelling error.
+
+Another reason for this could be a name clash. It is not possible to have
+config names like e.g. stats.py - because that colides with MoinMoin/stats/ -
+have a look into your MoinMoin code directory what other names are NOT
+possible.
+
+Please check your configuration file. As an example for correct syntax,
+use the wikiconfig.py file from the distribution.
+''' % {
+    'name': name,
+    'err': err,
+}
+        raise error.ConfigurationError(msg)
+    return cfg
+
+
+def _getConfigName(url):
+    """ Return config name for url or raise """
+    for name, regex in _url_re_list():
+        match = regex.match(url)
+        if match:
+            return name
+    # nothing matched
+    msg = '''
+Could not find a match for url: "%(url)s".
+
+Check your URL regular expressions in the "wikis" list in
+"farmconfig.py". 
+''' % {
+    'url': url,
+}
+    raise error.ConfigurationError(msg)
+
+
+def getConfig(url):
+    """ Return cached config instance for url or create new one
+
+    If called by many threads in the same time multiple config
+    instances might be created. The first created item will be
+    returned, using dict.setdefault.
+
+    @param url: the url from request, possibly matching specific wiki
+    @rtype: DefaultConfig subclass instance
+    @return: config object for specific wiki
+    """
+    configName = _getConfigName(url)
+    try:
+        config = _config_cache[configName]
+    except KeyError:
+        config = _makeConfig(configName)
+        config = _config_cache.setdefault(configName, config)
+    return config
+
+
+# This is a way to mark some text for the gettext tools so that they don't
+# get orphaned. See http://www.python.org/doc/current/lib/node278.html.
+def _(text): return text
+
+
+class DefaultConfig:
+    """ default config values """
+
+    # All acl_rights_* lines must use unicode!
+    acl_rights_default = u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write"
+    acl_rights_before = u""
+    acl_rights_after = u""
+    acl_rights_valid = ['read', 'write', 'delete', 'revert', 'admin']
+
+    actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage', 'test', ]
+    allow_xslt = 0
+    attachments = None # {'dir': path, 'url': url-prefix}
+    auth = [authmodule.moin_login, authmodule.moin_session, ]
+
+    backup_compression = 'gz'
+    backup_users = []
+    backup_include = []
+    backup_exclude = [
+        r"(.+\.py(c|o)$)",
+        r"%(cache_dir)s",
+        r"%(/)spages%(/)s.+%(/)scache%(/)s[^%(/)s]+$" % {'/': os.sep},
+        r"%(/)s(edit-lock|event-log|\.DS_Store)$" % {'/': os.sep},
+        ]
+    backup_storage_dir = '/tmp'
+    backup_restore_target_dir = '/tmp'
+
+    bang_meta = 1
+    caching_formats = ['text_html']
+    changed_time_fmt = '%H:%M'
+
+    # chars_{upper,lower,digits,spaces} see MoinMoin/util/chartypes.py
+
+    # if you have gdchart, add something like
+    # chart_options = {'width = 720, 'height': 540}
+    chart_options = None
+
+    config_check_enabled = 0
+
+    cookie_domain = None # use '.domain.tld" for a farm with hosts in that domain
+    cookie_path = None   # use '/wikifarm" for a farm with pathes below that path
+    cookie_lifetime = 12 # 12 hours from now
+    cookie_secret = '1234' # secret value for crypting session cookie - you should change this :)
+
+    data_dir = './data/'
+    data_underlay_dir = './underlay/'
+
+    date_fmt = '%Y-%m-%d'
+    datetime_fmt = '%Y-%m-%d %H:%M:%S'
+
+    default_markup = 'wiki'
+    docbook_html_dir = r"/usr/share/xml/docbook/stylesheet/nwalsh/html/" # correct for debian sarge
+
+    editor_default = 'text' # which editor is called when nothing is specified
+    editor_ui = 'freechoice' # which editor links are shown on user interface
+    editor_force = False
+    editor_quickhelp = {# editor markup hints quickhelp 
+        'wiki': _("""\
+ Emphasis:: [[Verbatim('')]]''italics''[[Verbatim('')]]; [[Verbatim(''')]]'''bold'''[[Verbatim(''')]]; [[Verbatim(''''')]]'''''bold italics'''''[[Verbatim(''''')]]; [[Verbatim('')]]''mixed ''[[Verbatim(''')]]'''''bold'''[[Verbatim(''')]] and italics''[[Verbatim('')]]; [[Verbatim(----)]] horizontal rule.
+ Headings:: [[Verbatim(=)]] Title 1 [[Verbatim(=)]]; [[Verbatim(==)]] Title 2 [[Verbatim(==)]]; [[Verbatim(===)]] Title 3 [[Verbatim(===)]];   [[Verbatim(====)]] Title 4 [[Verbatim(====)]]; [[Verbatim(=====)]] Title 5 [[Verbatim(=====)]].
+ Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1.#n start numbering at n; space alone indents.
+ Links:: [[Verbatim(JoinCapitalizedWords)]]; [[Verbatim(["brackets and double quotes"])]]; url; [url]; [url label].
+ Tables:: || cell text |||| cell text spanning 2 columns ||;    no trailing white space allowed after tables or titles.
+
+(!) For more help, see HelpOnEditing or SyntaxReference.
+"""),
+        'rst': _("""\
+Emphasis: <i>*italic*</i> <b>**bold**</b> ``monospace``<br/>
+<br/><pre>
+Headings: Heading 1  Heading 2  Heading 3
+          =========  ---------  ~~~~~~~~~
+
+Horizontal rule: ---- 
+Links: TrailingUnderscore_ `multi word with backticks`_ external_ 
+
+.. _external: http://external-site.net/foo/
+
+Lists: * bullets; 1., a. numbered items.
+</pre>
+<br/>
+(!) For more help, see the 
+<a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">
+reStructuredText Quick Reference
+</a>.
+"""),
+    }
+    edit_locking = 'warn 10' # None, 'warn <timeout mins>', 'lock <timeout mins>'
+    edit_rows = 20
+
+    hacks = {} # { 'feature1': value1, ... }
+               # Configuration for features still in development.
+               # For boolean stuff just use config like this:
+               #   hacks = { 'feature': True, ...}
+               # and in the code use:
+               #   if cfg.hacks.get('feature', False): <doit>
+               # A non-existing hack key should ever mean False, None, "", [] or {}!
+
+    hosts_deny = []
+
+    html_head = ''
+    html_head_queries = '''<meta name="robots" content="noindex,nofollow">\n'''
+    html_head_posts   = '''<meta name="robots" content="noindex,nofollow">\n'''
+    html_head_index   = '''<meta name="robots" content="index,follow">\n'''
+    html_head_normal  = '''<meta name="robots" content="index,nofollow">\n'''
+    html_pagetitle = None
+
+    interwiki_preferred = [] # list of wiki names to show at top of interwiki list
+
+    language_default = 'en'
+    language_ignore_browser = False # ignore browser settings, use language_default
+                                    # or user prefs
+
+    log_reverse_dns_lookups = True  # if we do reverse dns lookups for logging hostnames
+                                    # instead of just IPs
+
+    xapian_search = False # disabled until xapian is finished
+    xapian_index_dir = None
+    xapian_stemming = True
+
+    mail_login = None # or "user pwd" if you need to use SMTP AUTH
+    mail_sendmail = None # "/usr/sbin/sendmail -t -i" to not use SMTP, but sendmail
+    mail_smarthost = None
+    mail_from = None # u'Juergen Wiki <noreply@jhwiki.org>'
+
+    mail_import_subpage_template = u"$from-$date-$subject" # used for mail import
+    mail_import_wiki_address = None # the e-mail address for e-mails that should go into the wiki
+    mail_import_secret = ""
+
+    navi_bar = [u'RecentChanges', u'FindPage', u'HelpContents', ]
+    nonexist_qm = 0
+
+    page_credits = [
+        '<a href="http://moinmoin.wikiwikiweb.de/">MoinMoin Powered</a>',
+        '<a href="http://www.python.org/">Python Powered</a>',
+        '<a href="http://validator.w3.org/check?uri=referer">Valid HTML 4.01</a>',
+        ]
+    page_footer1 = ''
+    page_footer2 = ''
+
+    page_header1 = ''
+    page_header2 = ''
+
+    page_front_page = u'HelpOnLanguages' # this will make people choose a sane config
+    page_local_spelling_words = u'LocalSpellingWords'
+    page_category_regex = u'^Category[A-Z]'
+    page_dict_regex = u'[a-z0-9]Dict$'
+    page_group_regex = u'[a-z0-9]Group$'
+    page_template_regex = u'[a-z0-9]Template$'
+
+    page_license_enabled = 0
+    page_license_page = u'WikiLicense'
+
+    # These icons will show in this order in the iconbar, unless they
+    # are not relevant, e.g email icon when the wiki is not configured
+    # for email.
+    page_iconbar = ["up", "edit", "view", "diff", "info", "subscribe", "raw", "print", ]
+
+    # Standard buttons in the iconbar
+    page_icons_table = {
+        # key           last part of url, title, icon-key
+        'help':        ("%(q_page_help_contents)s", "%(page_help_contents)s", "help"),
+        'find':        ("%(q_page_find_page)s?value=%(q_page_name)s", "%(page_find_page)s", "find"),
+        'diff':        ("%(q_page_name)s?action=diff", _("Diffs"), "diff"),
+        'info':        ("%(q_page_name)s?action=info", _("Info"), "info"),
+        'edit':        ("%(q_page_name)s?action=edit", _("Edit"), "edit"),
+        'unsubscribe': ("%(q_page_name)s?action=subscribe", _("UnSubscribe"), "unsubscribe"),
+        'subscribe':   ("%(q_page_name)s?action=subscribe", _("Subscribe"), "subscribe"),
+        'raw':         ("%(q_page_name)s?action=raw", _("Raw"), "raw"),
+        'xml':         ("%(q_page_name)s?action=show&amp;mimetype=text/xml", _("XML"), "xml"),
+        'print':       ("%(q_page_name)s?action=print", _("Print"), "print"),
+        'view':        ("%(q_page_name)s", _("View"), "view"),
+        'up':          ("%(q_page_parent_page)s", _("Up"), "up"),
+        }
+
+    refresh = None # (minimum_delay, type), e.g.: (2, 'internal')
+    rss_cache = 60 # suggested caching time for RecentChanges RSS, in seconds
+    shared_intermap = None # can be string or list of strings (filenames)
+    show_hosts = 1
+    show_interwiki = 0
+    show_login = 1
+    show_names = True
+    show_section_numbers = 0
+    show_timings = 0
+    show_version = 0
+    siteid = 'default'
+    stylesheets = [] # list of tuples (media, csshref) to insert after theme css, before user css
+    superuser = [] # list of unicode user names that have super powers :)
+
+    surge_action_limits = {# allow max. <count> <action> requests per <dt> secs
+        # action: (count, dt)
+        'all': (30, 30),
+        'show': (30, 60),
+        'recall': (5, 60),
+        'raw': (20, 40),  # some people use this for css
+        'AttachFile': (90, 60),
+        'diff': (30, 60),
+        'fullsearch': (5, 60),
+        'edit': (10, 120),
+        'rss_rc': (1, 60),
+        'default': (30, 60),
+    }
+    surge_lockout_time = 3600 # secs you get locked out when you ignore warnings
+
+    theme_default = 'modern'
+    theme_force = False
+
+    trail_size = 5
+    tz_offset = 0.0 # default time zone offset in hours from UTC
+
+    user_autocreate = False # do we auto-create user profiles
+    user_email_unique = True # do we check whether a user's email is unique?
+
+    # a regex of HTTP_USER_AGENTS that should be excluded from logging
+    # and receive a FORBIDDEN for anything except viewing a page
+    ua_spiders = ('archiver|cfetch|crawler|curl|gigabot|googlebot|holmes|htdig|httrack|httpunit|jeeves|larbin|leech|'
+                  'linkbot|linkmap|linkwalk|mercator|mirror|msnbot|nutbot|omniexplorer|puf|robot|scooter|'
+                  'sherlock|slurp|sitecheck|spider|teleport|voyager|webreaper|wget')
+
+    # Wiki identity
+    sitename = u'Untitled Wiki'
+    url_prefix = '/wiki'
+    logo_string = None
+    interwikiname = None
+
+    url_mappings = {}
+
+    user_checkbox_fields = [
+        ('mailto_author', lambda _: _('Publish my email (not my wiki homepage) in author info')),
+        ('edit_on_doubleclick', lambda _: _('Open editor on double click')),
+        ('remember_last_visit', lambda _: _('After login, jump to last visited page')),
+        ('show_nonexist_qm', lambda _: _('Show question mark for non-existing pagelinks')),
+        ('show_page_trail', lambda _: _('Show page trail')),
+        ('show_toolbar', lambda _: _('Show icon toolbar')),
+        ('show_topbottom', lambda _: _('Show top/bottom links in headings')),
+        ('show_fancy_diff', lambda _: _('Show fancy diffs')),
+        ('wikiname_add_spaces', lambda _: _('Add spaces to displayed wiki names')),
+        ('remember_me', lambda _: _('Remember login information')),
+        ('want_trivial', lambda _: _('Subscribe to trivial changes')),
+
+        ('disabled', lambda _: _('Disable this account forever')),
+        # if an account is disabled, it may be used for looking up
+        # id -> username for page info and recent changes, but it
+        # is not usable for the user any more:
+    ]
+
+    user_checkbox_defaults = {'mailto_author':       0,
+                              'edit_on_doubleclick': 0,
+                              'remember_last_visit': 0,
+                              'show_nonexist_qm':    nonexist_qm,
+                              'show_page_trail':     1,
+                              'show_toolbar':        1,
+                              'show_topbottom':      0,
+                              'show_fancy_diff':     1,
+                              'wikiname_add_spaces': 0,
+                              'remember_me':         1,
+                              'want_trivial':        0,
+                             }
+
+    # don't let the user change those
+    # user_checkbox_disable = ['disabled', 'want_trivial']
+    user_checkbox_disable = []
+
+    # remove those checkboxes:
+    #user_checkbox_remove = ['edit_on_doubleclick', 'show_nonexist_qm', 'show_toolbar', 'show_topbottom',
+    #                        'show_fancy_diff', 'wikiname_add_spaces', 'remember_me', 'disabled',]
+    user_checkbox_remove = []
+
+    user_form_fields = [
+        ('name', _('Name'), "text", "36", _("(Use Firstname''''''Lastname)")),
+        ('aliasname', _('Alias-Name'), "text", "36", ''),
+        ('password', _('Password'), "password", "36", ''),
+        ('password2', _('Password repeat'), "password", "36", _('(Only for password change or new account)')),
+        ('email', _('Email'), "text", "36", ''),
+        ('css_url', _('User CSS URL'), "text", "40", _('(Leave it empty for disabling user CSS)')),
+        ('edit_rows', _('Editor size'), "text", "3", ''),
+    ]
+
+    user_form_defaults = {# key: default - do NOT remove keys from here!
+        'name': '',
+        'aliasname': '',
+        'password': '',
+        'password2': '',
+        'email': '',
+        'css_url': '',
+        'edit_rows': "20",
+    }
+
+    # don't let the user change those, but show them:
+    #user_form_disable = ['name', 'aliasname', 'email',]
+    user_form_disable = []
+
+    # remove those completely:
+    #user_form_remove = ['password', 'password2', 'css_url', 'logout', 'create', 'account_sendmail',]
+    user_form_remove = []
+
+    # attributes we do NOT save to the userpref file
+    user_transient_fields = ['id', 'valid', 'may', 'auth_username', 'trusted', 'password', 'password2', 'auth_method', 'auth_attribs', ]
+
+    user_homewiki = 'Self' # interwiki name for where user homepages are located
+
+    unzip_single_file_size = 2.0 * 1000**2
+    unzip_attachments_space = 200.0 * 1000**2
+    unzip_attachments_count = 51 # 1 zip file + 50 files contained in it
+
+    xmlrpc_putpage_enabled = 0 # if 0, putpage will write to a test page only
+    xmlrpc_putpage_trusted_only = 1 # if 1, you will need to be http auth authenticated
+
+    SecurityPolicy = None
+
+    def __init__(self, siteid):
+        """ Init Config instance """
+        self.siteid = siteid
+        if self.config_check_enabled:
+            self._config_check()
+
+        # define directories
+        self.moinmoin_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))
+        data_dir = os.path.normpath(self.data_dir)
+        self.data_dir = data_dir
+        for dirname in ('user', 'cache', 'plugin'):
+            name = dirname + '_dir'
+            if not getattr(self, name, None):
+                setattr(self, name, os.path.join(data_dir, dirname))
+
+        # Try to decode certain names which allow unicode
+        self._decode()
+
+        self._check_directories()
+
+        if not isinstance(self.superuser, list):
+            msg = """The superuser setting in your wiki configuration is not a list
+                     (e.g. ['Sample User', 'AnotherUser']).
+                     Please change it in your wiki configuration and try again."""
+            raise error.ConfigurationError(msg)
+
+        self._loadPluginModule()
+
+        # Preparse user dicts
+        self._fillDicts()
+
+        # Normalize values
+        self.language_default = self.language_default.lower()
+
+        # Use site name as default name-logo
+        if self.logo_string is None:
+            self.logo_string = self.sitename
+
+        # Check for needed modules
+
+        # FIXME: maybe we should do this check later, just before a
+        # chart is needed, maybe in the chart module, instead doing it
+        # for each request. But this require a large refactoring of
+        # current code.
+        if self.chart_options:
+            try:
+                import gdchart
+            except ImportError:
+                self.chart_options = None
+
+        # post process
+        # we replace any string placeholders with config values
+        # e.g u'%(page_front_page)s' % self
+        self.navi_bar = [elem % self for elem in self.navi_bar]
+        self.backup_exclude = [elem % self for elem in self.backup_exclude]
+
+        # list to cache xapian searcher objects
+        self.xapian_searchers = []
+
+        # check if mail is possible and set flag:
+        self.mail_enabled = (self.mail_smarthost is not None or self.mail_sendmail is not None) and self.mail_from
+
+    def _config_check(self):
+        """ Check namespace and warn about unknown names
+        
+        Warn about names which are not used by DefaultConfig, except
+        modules, classes, _private or __magic__ names.
+
+        This check is disabled by default, when enabled, it will show an
+        error message with unknown names.
+        """
+        unknown = ['"%s"' % name for name in dir(self)
+                  if not name.startswith('_') and
+                  not DefaultConfig.__dict__.has_key(name) and
+                  not isinstance(getattr(self, name), (type(sys), type(DefaultConfig)))]
+        if unknown:
+            msg = """
+Unknown configuration options: %s.
+
+For more information, visit HelpOnConfiguration. Please check your
+configuration for typos before requesting support or reporting a bug.
+""" % ', '.join(unknown)
+            raise error.ConfigurationError(msg)
+
+    def _decode(self):
+        """ Try to decode certain names, ignore unicode values
+        
+        Try to decode str using utf-8. If the decode fail, raise FatalError. 
+
+        Certain config variables should contain unicode values, and
+        should be defined with u'text' syntax. Python decode these if
+        the file have a 'coding' line.
+        
+        This will allow utf-8 users to use simple strings using, without
+        using u'string'. Other users will have to use u'string' for
+        these names, because we don't know what is the charset of the
+        config files.
+        """
+        charset = 'utf-8'
+        message = u'''
+"%(name)s" configuration variable is a string, but should be
+unicode. Use %(name)s = u"value" syntax for unicode variables.
+
+Also check your "-*- coding -*-" line at the top of your configuration
+file. It should match the actual charset of the configuration file.
+'''
+
+        decode_names = (
+            'sitename', 'logo_string', 'navi_bar', 'page_front_page',
+            'page_category_regex', 'page_dict_regex',
+            'page_group_regex', 'page_template_regex', 'page_license_page',
+            'page_local_spelling_words', 'acl_rights_default',
+            'acl_rights_before', 'acl_rights_after', 'mail_from'
+            )
+
+        for name in decode_names:
+            attr = getattr(self, name, None)
+            if attr:
+                # Try to decode strings
+                if isinstance(attr, str):
+                    try:
+                        setattr(self, name, unicode(attr, charset))
+                    except UnicodeError:
+                        raise error.ConfigurationError(message %
+                                                       {'name': name})
+                # Look into lists and try to decode strings inside them
+                elif isinstance(attr, list):
+                    for i in xrange(len(attr)):
+                        item = attr[i]
+                        if isinstance(item, str):
+                            try:
+                                attr[i] = unicode(item, charset)
+                            except UnicodeError:
+                                raise error.ConfigurationError(message %
+                                                               {'name': name})
+
+    def _check_directories(self):
+        """ Make sure directories are accessible
+
+        Both data and underlay should exists and allow read, write and
+        execute.
+        """
+        mode = os.F_OK | os.R_OK | os.W_OK | os.X_OK
+        for attr in ('data_dir', 'data_underlay_dir'):
+            path = getattr(self, attr)
+
+            # allow an empty underlay path or None
+            if attr == 'data_underlay_dir' and not path:
+                continue
+
+            path_pages = os.path.join(path, "pages")
+            if not (os.path.isdir(path_pages) and os.access(path_pages, mode)):
+                msg = '''
+%(attr)s "%(path)s" does not exists, or has incorrect ownership or
+permissions.
+
+Make sure the directory and the subdirectory pages are owned by the web
+server and are readable, writable and executable by the web server user
+and group.
+
+It is recommended to use absolute paths and not relative paths. Check
+also the spelling of the directory name.
+''' % {'attr': attr, 'path': path, }
+                raise error.ConfigurationError(msg)
+
+    def _loadPluginModule(self):
+        """ import plugin module under configname.plugin
+
+        To be able to import plugin from arbitrary path, we have to load
+        the base package once using imp.load_module. Later, we can use
+        standard __import__ call to load plugins in this package.
+
+        Since each wiki has unique plugins, we load the plugin package
+        under the wiki configuration module, named self.siteid.
+        """
+        import sys, imp
+
+        name = self.siteid + '.plugin'
+        try:
+            # Lock other threads while we check and import
+            imp.acquire_lock()
+            try:
+                # If the module is not loaded, try to load it
+                if not name in sys.modules:
+                    # Find module on disk and try to load - slow!
+                    plugin_parent_dir = os.path.abspath(os.path.join(self.plugin_dir, '..'))
+                    fp, path, info = imp.find_module('plugin', [plugin_parent_dir])
+                    try:
+                        # Load the module and set in sys.modules             
+                        module = imp.load_module(name, fp, path, info)
+                        sys.modules[self.siteid].plugin = module
+                    finally:
+                        # Make sure fp is closed properly
+                        if fp:
+                            fp.close()
+            finally:
+                imp.release_lock()
+        except ImportError, err:
+            msg = '''
+Could not import plugin package "%(path)s/plugin" because of ImportError:
+%(err)s.
+
+Make sure your data directory path is correct, check permissions, and
+that the data/plugin directory has an __init__.py file.
+''' % {
+    'path': self.data_dir,
+    'err': str(err),
+}
+            raise error.ConfigurationError(msg)
+
+    def _fillDicts(self):
+        """ fill config dicts
+
+        Fills in missing dict keys of derived user config by copying
+        them from this base class.
+        """
+        # user checkbox defaults
+        for key, value in DefaultConfig.user_checkbox_defaults.items():
+            if not self.user_checkbox_defaults.has_key(key):
+                self.user_checkbox_defaults[key] = value
+
+    def __getitem__(self, item):
+        """ Make it possible to access a config object like a dict """
+        return getattr(self, item)
+
+# remove the gettext pseudo function 
+del _
+
--- a/MoinMoin/converter/text_html_text_moin_wiki.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/converter/text_html_text_moin_wiki.py	Mon Jul 24 22:18:49 2006 +0200
@@ -407,7 +407,7 @@
 class ConvertError(error.FatalError):
     """ Raise when html to wiki conversion fails """
     name = "MoinMoin Convert Error"
-    
+
 
 class visitor(object):
     def do(self, tree):
@@ -434,7 +434,7 @@
 
     def visit_attribute(self, node):
         pass
-	
+
     def visit_text(self, node):
         pass
 
@@ -469,7 +469,7 @@
     def __init__(self, request, pagename):
         self.request = request
         self.pagename = pagename
-    
+
     def do(self, tree):
         self.depth = 0
         self.text = []
@@ -520,7 +520,7 @@
         name = node.localName
         if name is None: # not sure this can happen here (DOM comment node), but just for the case
             return
-        func = getattr(self, "process_%s" % name,  None)
+        func = getattr(self, "process_%s" % name, None)
         if func:
             func(node)
         else:
@@ -565,7 +565,7 @@
             self.text.append(self.new_line)
             self.text.append("%s %s %s" % (hstr, text.replace("\n", " "), hstr))
             self.text.append(self.new_line)
-    
+
     process_h1 = process_heading
     process_h2 = process_heading
     process_h3 = process_heading
@@ -593,7 +593,7 @@
             if class_ == "gap":
                 before = "\n"
             style = listitem.getAttribute("style")
-            if re.match(u"list-style-type:\s*none", style, re.I):
+            if re.match(ur"list-style-type:\s*none", style, re.I):
                 markup = ". "
             else:
                 markup = "* "
@@ -666,7 +666,7 @@
             if name in ('p', 'pre', 'ol', 'ul', 'dl', 'table',) and pending:
                 self.empty_paragraph_queue(pending, indent, need_indent)
                 need_indent = True
-                
+
             if name == 'p':
                 if need_indent:
                     self.text.append(indent)
@@ -737,7 +737,7 @@
 
     def process_inline(self, node):
         if node.nodeType == Node.TEXT_NODE:
-            self.text.append(node.data.strip('\n').replace('\n',' '))
+            self.text.append(node.data.strip('\n').replace('\n', ' '))
             return
 
         name = node.localName # can be None for DOM Comment nodes
@@ -748,8 +748,8 @@
             text = self.node_list_text_only(node.childNodes).strip() # but can be inserted via the editor
             self.text.append(text)                          # so we just drop the header markup and keep the text
             return
-        
-        func = getattr(self, "process_%s" % name,  None)
+
+        func = getattr(self, "process_%s" % name, None)
         if func:
             func(node)
             return
@@ -778,13 +778,13 @@
             command = "" # just throw away font settings
         else:
             raise ConvertError("process_inline: Don't support %s element" % name)
-        
+
         self.text.append(command)
         for i in node.childNodes:
             if i.nodeType == Node.ELEMENT_NODE:
                 self.process_inline(i)
             elif i.nodeType == Node.TEXT_NODE:
-                self.text.append(i.data.strip('\n').replace('\n',' '))
+                self.text.append(i.data.strip('\n').replace('\n', ' '))
         if command_close:
             command = command_close
         self.text.append(command)
@@ -865,11 +865,11 @@
                     #print i.localName
             self.text.extend(["}}}", self.new_line])
 
-    _alignment = {"left" : "(",
-                  "center" : ":",
-                  "right" : ")",
-                  "top" : "^",
-                  "bottom" : "v"}
+    _alignment = {"left": "(",
+                  "center": ":",
+                  "right": ")",
+                  "top": "^",
+                  "bottom": "v"}
 
     def _check_length(self, value):
         try:
@@ -933,7 +933,7 @@
             colspan = 1
 
         spanning = rowspan or colspan > 1
-        
+
         align = ""
         result = []
         if  node.hasAttribute("bgcolor"):
@@ -970,7 +970,7 @@
             result.append('id="%s"' % node.getAttribute("id"))
         if node.hasAttribute("style"):
             result.append('style="%s"' % node.getAttribute("style"))
-                
+
         if align:
             result.insert(0, "%s" % align)
         result.append(rowspan)
@@ -1049,7 +1049,7 @@
                     self.process_inline(i)
                     found = True
                 elif i.nodeType == Node.TEXT_NODE:
-                    data = i.data.strip('\n').replace('\n',' ')
+                    data = i.data.strip('\n').replace('\n', ' ')
                     if data:
                         found = True
                         self.text.append(data)
@@ -1083,7 +1083,7 @@
         id = node.attributes.get("id", None)
         if id:
             id = id.nodeValue
-        
+
         if href:
             title = class_ = interwikiname = None
 
@@ -1102,14 +1102,14 @@
                 if not err and href.startswith(wikiurl):
                     pagename, qpagename = pagename_from_url(href[len(wikiurl):].lstrip('/'))
                     interwikiname = "%s:%s" % (wikitag, qpagename)
-                else: 
+                else:
                     raise ConvertError("Invalid InterWiki link: '%s'" % href)
             elif class_ == "badinterwiki" and title:
                 if href == "/": # we used this as replacement for empty href
                     href = ""
                 pagename, qpagename = pagename_from_url(href)
                 interwikiname = "%s:%s" % (title, qpagename)
-            if interwikiname and pagename == text: 
+            if interwikiname and pagename == text:
                 self.text.append("%s" % interwikiname)
                 return
             elif title == 'Self':
@@ -1118,7 +1118,7 @@
             elif interwikiname:
                 self.text.append("[wiki:%s %s]" % (interwikiname, text))
                 return
-            
+
             # fix links generated by a broken copy & paste of gecko based browsers
             brokenness = '../../../..'
             if href.startswith(brokenness):
--- a/MoinMoin/filter/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/filter/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -12,7 +12,7 @@
 filters = pysupport.getPackageModules(__file__)
 modules = filters
 
-standard_codings = ['utf-8', 'iso-8859-15', 'iso-8859-1',]
+standard_codings = ['utf-8', 'iso-8859-15', 'iso-8859-1', ]
 
 def execfilter(cmd, filename, codings=standard_codings):
     """ use cmd to get plaintext content of filename
--- a/MoinMoin/filter/application_octet_stream.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/filter/application_octet_stream.py	Mon Jul 24 22:18:49 2006 +0200
@@ -36,7 +36,7 @@
 norm = string.maketrans('', '')
 
 # builds a list of all non-alphanumeric characters:
-non_alnum = string.translate(norm, norm, string.letters+string.digits) 
+non_alnum = string.translate(norm, norm, string.letters+string.digits)
 
 # translate table that replaces all non-alphanumeric by blanks:
 trans_nontext = string.maketrans(non_alnum, ' '*len(non_alnum))
--- a/MoinMoin/filter/text.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/filter/text.py	Mon Jul 24 22:18:49 2006 +0200
@@ -24,5 +24,4 @@
     f.close()
     data = data.decode('ascii', 'replace')
     return data
-    
 
--- a/MoinMoin/formatter/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -60,7 +60,7 @@
         return ""
 
     # Document Level #####################################################
-    
+
     def startDocument(self, pagename):
         return ""
 
@@ -74,13 +74,13 @@
         return ""
 
     # Links ##############################################################
-    
+
     def pagelink(self, on, pagename='', page=None, **kw):
         """ make a link to page <pagename>. Instead of supplying a pagename,
             it is also possible to give a live Page object, then page.page_name
             will be used.
         """
-        if not self._store_pagelinks or not on or kw.get('generated'): 
+        if not self._store_pagelinks or not on or kw.get('generated'):
             return ''
         if not pagename and page:
             pagename = page.page_name
@@ -101,7 +101,7 @@
                 wikitail = wikiutil.url_unquote(wikitail)
             return self.pagelink(on, wikitail, **kw)
         return ''
-            
+
     def url(self, on, url=None, css=None, **kw):
         raise NotImplementedError
 
@@ -171,11 +171,11 @@
         return self.text(text)
 
     # Text and Text Attributes ########################################### 
-    
+
     def text(self, text, **kw):
         if not self._highlight_re:
             return self._text(text)
-            
+
         result = []
         lastpos = 0
         match = self._highlight_re.search(text)
@@ -278,7 +278,7 @@
         raise NotImplementedError
 
     # Tables #############################################################
-    
+
     def table(self, on, attrs={}, **kw):
         raise NotImplementedError
 
@@ -289,10 +289,10 @@
         raise NotImplementedError
 
     # Dynamic stuff / Plugins ############################################
-    
+
     def macro(self, macro_obj, name, args):
         # call the macro
-        return macro_obj.execute(name, args)    
+        return macro_obj.execute(name, args)
 
     def _get_bang_args(self, line):
         if line[:2] == '#!':
@@ -326,15 +326,15 @@
         return ''
 
     # Other ##############################################################
-    
+
     def div(self, on, **kw):
         """ open/close a blocklevel division """
         return ""
-    
+
     def span(self, on, **kw):
         """ open/close a inline span """
         return ""
-    
+
     def rawHTML(self, markup):
         """ This allows emitting pre-formatted HTML markup, and should be
             used wisely (i.e. very seldom).
--- a/MoinMoin/formatter/dom_xml.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/dom_xml.py	Mon Jul 24 22:18:49 2006 +0200
@@ -57,7 +57,7 @@
         'ol': ['li'],
         'ul': ['li'],
         }
-    
+
     # FIXME - this overrides the values defined above - FIXME XXX
     close_on_open = {}
     close_on_close = {}
@@ -69,7 +69,7 @@
         self.document.documentElement = self.document.createElement('xml')
         self.position = self.document.documentElement
         self.tag_stack = [('xml', {})]
-        
+
     def setPage(self, page):
         self.page = page
 
@@ -110,7 +110,7 @@
         for name, value in attrs.items():
             if value:
                 node.setAttribute(name, str(value))
-        self.position.appendChild(node)                        
+        self.position.appendChild(node)
         return ''
 
     def _text(self, text):
@@ -195,13 +195,13 @@
         if not pagename and page is not None:
             pagename = page.page_name
         kw['pagename'] = pagename
-        return self._set_tag('pagelink', on,  **kw)
+        return self._set_tag('pagelink', on, **kw)
 
     def interwikilink(self, on, interwiki='', pagename='', **kw):
         kw['wiki'] = interwiki
         kw['pagename'] = pagename
         return self._set_tag('interwiki', on, **kw)
-    
+
     def macro(self, macro_obj, name, args):
         # call the macro
         return self._add_tag('macro', name=name, args=(args or ''))
@@ -227,17 +227,17 @@
         kw['href'] = url
         kw['type'] = 'link'
         return self._set_tag('attachment', 1, **kw) + self.text(text) + self._set_tag('attachment', 0, **kw)
-    
+
     def attachment_image(self, url, **kw):
         kw['href'] = url
         kw['type'] = 'image'
         return self._add_tag('attachment', **kw)
-    
+
     def attachment_drawing(self, url, **kw):
         kw['href'] = url
         kw['type'] = 'drawing'
         return self._add_tag('attachment', **kw)
-    
+
     def attachment_inlined(self, url, **kw):
         kw['href'] = url
         kw['type'] = 'inline'
@@ -295,7 +295,7 @@
             return self.text('\n')
         else:
             return self._add_tag('br')
-                                  
+
     def heading(self, on, depth, **kw):
         return self._set_tag('h%d' %depth, on, **kw)
 
@@ -307,7 +307,7 @@
 
     def table(self, on, attrs={}, **kw):
         return self._set_tag('table', on, **self._check_attrs(attrs))
-        
+
     def table_row(self, on, attrs={}, **kw):
         return self._set_tag('tr', on, **self._check_attrs(attrs))
 
@@ -338,7 +338,7 @@
         if compact and on:
             return self._set_tag('dt compact', on)
         else:
-            return self._set_tag('dt', on)            
+            return self._set_tag('dt', on)
 
     def definition_desc(self, on, **kw):
         # self._langAttr() missing
--- a/MoinMoin/formatter/pagelinks.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/pagelinks.py	Mon Jul 24 22:18:49 2006 +0200
@@ -9,19 +9,19 @@
 from MoinMoin.formatter import FormatterBase
 
 class Formatter(FormatterBase):
-    """ Collect pagelinks and format nothing :-) """        
-    
+    """ Collect pagelinks and format nothing :-) """
+
     def pagelink(self, on, pagename='', page=None, **kw):
         FormatterBase.pagelink(self, on, pagename, page, **kw)
         return self.null()
- 
+
     def null(self, *args, **kw):
         return ''
-    
+
     # All these must be overriden here because they raise
     # NotImplementedError!@#! or return html?! in the base class.
     set_highlight_re = rawHTML = url = image = smiley = text = null
-    strong = emphasis = underline =  highlight = sup = sub = strike = null
+    strong = emphasis = underline = highlight = sup = sub = strike = null
     code = preformatted = small = big = code_area = code_line = null
     code_token = linebreak = paragraph = rule = icon = null
     number_list = bullet_list = listitem = definition_list = null
--- a/MoinMoin/formatter/text_docbook.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/text_docbook.py	Mon Jul 24 22:18:49 2006 +0200
@@ -30,15 +30,15 @@
     """
        Format docbook output
     """
-    
+
     def __init__(self, dommDoc):
         self.doc = dommDoc
         self.curNode = dommDoc.documentElement
-        
+
     def setHeading(self, headNode):
         self.domHeadNode = headNode
         return u""
-    
+
     def _printNode(self, node):
         """
             Function print a node
@@ -46,15 +46,15 @@
         from xml.dom.ext import Print
         import StringIO
         from xml.dom.ext import Printer
-        
+
         stream = StringIO.StringIO()
-        
+
         visitor = Printer.PrintVisitor(stream, 'UTF-8')
         Printer.PrintWalker(visitor, node).run()
         # get value from stream
         ret = stream.getvalue()
         stream.close()
-        
+
         return unicode(ret, 'utf-8')
 
     def getHeading(self):
@@ -63,7 +63,7 @@
         # print article info
         return '<?xml version="1.0"?><%s>%s' % (rootNode.nodeName,
                                                 self._printNode(self.domHeadNode))
-        
+
     def getBody(self):
         body = []
         # print all nodes inside dom behind heading
@@ -81,7 +81,7 @@
             ret.append("</%s>" % (self.curNode.nodeName, ))
             self.curNode = self.curNode.parentNode
         return ''.join(ret)
-        
+
     def getFooter(self):
         return "</%s>" % self.doc.documentElement.nodeName
 
@@ -93,7 +93,7 @@
     section_should_break = ['abstract', 'para', 'emphasis']
 
     def __init__(self, request, **kw):
-        '''We should use this for creating the doc'''
+        """We should use this for creating the doc"""
         FormatterBase.__init__(self, request, **kw)
 
         self.doc = dom.createDocument(None, "article", dom.createDocumentType(
@@ -113,7 +113,7 @@
         self.root.appendChild(info)
         # set heading node
         self.outputFormatter.setHeading(info)
-        
+
         return self.outputFormatter.getHeading()
 
     def startContent(self, content_id="content", **kw):
@@ -138,7 +138,7 @@
         else:
             srcText = text
         if self.cur.nodeName == "screen":
-            if self.cur.lastChild != None:
+            if self.cur.lastChild is not None:
                 from xml.dom.ext import Node
                 if self.cur.lastChild.nodeType == Node.CDATA_SECTION_NODE:
                     self.cur.lastChild.nodeValue = self.cur.lastChild.nodeValue + srcText
@@ -151,7 +151,7 @@
     def heading(self, on, depth, **kw):
         while self.cur.nodeName in self.section_should_break:
             self.cur = self.cur.parentNode
-               
+
         if on:
             # try to go to higher level if needed
             if depth <= self.curdepth:
@@ -165,7 +165,7 @@
 # I don't understand this code - looks like unnecessary -- maybe it is used to gain some vertical space for large headings?
 #                    if len(self.cur.childNodes) < 3:
 #                       self._addEmptyNode("para")
-                    
+
                     # check if not top-level
                     if self.cur.nodeName != "article":
                         self.cur = self.cur.parentNode
@@ -225,7 +225,7 @@
         cols = 1
         if attrs and attrs.has_key('colspan'):
             s1 = attrs['colspan']
-            s1 = str(s1).replace('"','')
+            s1 = str(s1).replace('"', '')
             cols = int(s1)
         return cols
 
@@ -270,16 +270,16 @@
         return self._handleNode(name, on, attributes)
 
     def strong(self, on, **kw):
-        return self._handleFormatting("emphasis", on, (('role','strong'), ))
+        return self._handleFormatting("emphasis", on, (('role', 'strong'), ))
 
     def emphasis(self, on, **kw):
         return self._handleFormatting("emphasis", on)
 
     def underline(self, on, **kw):
-        return self._handleFormatting("emphasis", on, (('role','underline'), ))
+        return self._handleFormatting("emphasis", on, (('role', 'underline'), ))
 
     def highlight(self, on, **kw):
-        return self._handleFormatting("emphasis", on, (('role','highlight'), ))
+        return self._handleFormatting("emphasis", on, (('role', 'highlight'), ))
 
     def sup(self, on, **kw):
         return self._handleFormatting("superscript", on)
@@ -291,7 +291,7 @@
         # does not yield <strike> using the HTML XSLT files here ...
         # but seems to be correct
         return self._handleFormatting("emphasis", on,
-                                      (('role','strikethrough'), ))
+                                      (('role', 'strikethrough'), ))
 
     def code(self, on, **kw):
         return self._handleFormatting("code", on)
@@ -303,8 +303,8 @@
 ### Lists ###########################################################
 
     def number_list(self, on, type=None, start=None, **kw):
-        docbook_ol_types = {'1': "arabic", 
-                            'a': "loweralpha", 
+        docbook_ol_types = {'1': "arabic",
+                            'a': "loweralpha",
                             'A': "upperalpha",
                             'i': "lowerroman",
                             'I': "upperroman"}
@@ -320,22 +320,22 @@
         return self._handleNode("itemizedlist", on)
 
     def definition_list(self, on, **kw):
-        return self._handleNode("glosslist", on)        
+        return self._handleNode("glosslist", on)
 
     def definition_term(self, on, compact=0, **kw):
        # When on is false, we back out just on level. This is
        # ok because we know definition_desc gets called, and we
        # back out two levels there.
         if on:
-            entry=self.doc.createElement('glossentry')
-            term=self.doc.createElement('glossterm')
+            entry = self.doc.createElement('glossentry')
+            term = self.doc.createElement('glossterm')
             entry.appendChild(term)
             self.cur.appendChild(entry)
             self.cur = term
         else:
             self.cur = self.cur.parentNode
         return ""
-   
+
     def definition_desc(self, on, **kw):
         # We backout two levels when 'on' is false, to leave the glossentry stuff
         if on:
@@ -365,14 +365,14 @@
     # FIXME: This is even more crappy
     def interwikilink(self, on, interwiki='', pagename='', **kw):
         if not on:
-            return self.url(on,kw)
+            return self.url(on, kw)
 
         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:"%s"' % (interwiki, pagename))
         wikiurl = wikiutil.mapURL(self.request, wikiurl)
         href = wikiutil.join_wiki(wikiurl, wikitail)
 
         return self.url(on, href)
-            
+
     def url(self, on, url=None, css=None, **kw):
         return self._handleNode("ulink", on, (('url', url), ))
 
@@ -382,11 +382,11 @@
         return ""
 
     def anchorlink(self, on, name='', **kw):
-        id = kw.get('id',None)
+        id = kw.get('id', None)
         attrs = []
         if name != '':
             attrs.append(('endterm', name))
-        if id != None:
+        if id is not None:
             attrs.append(('linkend', id))
         elif name != '':
             attrs.append(('linkend', name))
@@ -463,14 +463,14 @@
                 break
         if title:
             txtcontainer = self.doc.createElement('textobject')
-            media.appendChild(txtcontainer)        
+            media.appendChild(txtcontainer)
             txtphrase = self.doc.createElement('phrase')
             txtphrase.appendChild(self.doc.createTextNode(title))
-            txtcontainer.appendChild(txtphrase)        
-        
+            txtcontainer.appendChild(txtphrase)
+
         self.cur.appendChild(media)
-        return ""        
- 
+        return ""
+
     def smiley(self, text):
         return self.request.theme.make_icon(text)
 
@@ -492,7 +492,7 @@
         self._handleNode("tgroup", on)
         self._handleNode("tbody", on)
         return ""
-    
+
     def table_row(self, on, attrs=None, **kw):
         self.table_current_row_cells = 0
         sanitized_attrs = []
@@ -526,12 +526,12 @@
         show = show and 'numbered' or 'unnumbered'
         if start < 1:
             start = 1
-        
+
         attrs = (('id', code_id),
                 ('linenumbering', show),
                 ('startinglinenumber', str(start)),
                 ('language', code_type),
-                ('format','linespecific'),
+                ('format', 'linespecific'),
                 )
         return self._handleFormatting("screen", on, attrs)
 
@@ -578,7 +578,7 @@
 
 ### Not supported ###################################################
 
-    def rule(self, size = 0, **kw):
+    def rule(self, size=0, **kw):
         return ""
 
     def small(self, on, **kw):
--- a/MoinMoin/formatter/text_gedit.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/text_gedit.py	Mon Jul 24 22:18:49 2006 +0200
@@ -42,7 +42,7 @@
         """
         apply(FormatterBase.pagelink, (self, on, pagename, page), kw)
         if page is None:
-            page = Page(self.request, pagename, formatter=self);
+            page = Page(self.request, pagename, formatter=self)
         return page.link_to(self.request, on=on, **kw)
 
     def interwikilink(self, on, interwiki='', pagename='', **kw):
@@ -69,7 +69,7 @@
         return (self.url(1, target, title="attachment:%s" % wikiutil.quoteWikinameURL(url)) +
                 self.text(text) +
                 self.url(0))
-    
+
     def attachment_image(self, url, **kw):
         _ = self.request.getText
         pagename = self.page.page_name
@@ -91,10 +91,10 @@
         return '<span style="background-color:#ffff11">!</span>' + self.text(text)
 
     # Dynamic stuff / Plugins ############################################
-    
+
     def macro(self, macro_obj, name, args):
         if args is not None:
-            result =  "[[%s(%s)]]" % (name, args)    
+            result = "[[%s(%s)]]" % (name, args)
         else:
             result = "[[%s]]" % name
         return '<span style="background-color:#ffff11">%s</span>' % result
@@ -182,7 +182,7 @@
             # Close table then div
             result.append(self._close('table'))
 
-        return ''.join(result)    
+        return ''.join(result)
 
     def comment(self, text, **kw):
         text = text.rstrip() # workaround for growing amount of blanks at EOL
@@ -205,10 +205,10 @@
         if on:
             return self._open(tag, allowed_attrs=[], **kw)
         return self._close(tag)
-                    
+
     def line_anchordef(self, lineno):
         return '' # not needed for gui editor feeding
-        
+
     def line_anchorlink(self, on, lineno=0):
         return '' # not needed for gui editor feeding
 
--- a/MoinMoin/formatter/text_html.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/text_html.py	Mon Jul 24 22:18:49 2006 +0200
@@ -119,9 +119,9 @@
     if ns == 'html':
         # We have an HTML attribute, fix according to DTD
         if name == 'content_type': # MIME type such as in <a> and <link> elements
-            name =  'type'
+            name = 'type'
         elif name == 'content_id': # moin historical convention
-            name =  'id'
+            name = 'id'
         elif name in ('css_class', 'css'): # to avoid python word 'class'
             name = 'class'
         elif name.startswith('on'): # event handler hook
@@ -226,7 +226,7 @@
                 return {}
 
         #attr = {'xml:lang': lang, 'lang': lang, 'dir': i18n.getDirection(lang),}
-        attr = {'lang': lang, 'dir': i18n.getDirection(lang),}
+        attr = {'lang': lang, 'dir': i18n.getDirection(lang), }
         return attr
 
     def _formatAttributes(self, attr=None, allowed_attrs=None, **kw):
@@ -335,12 +335,12 @@
         if tag in _blocks:
             # Block elements
             result = []
-            
+
             # Add language attributes, but let caller overide the default
             attributes = self._langAttr()
             if attr:
                 attributes.update(attr)
-            
+
             # Format
             attributes = self._formatAttributes(attributes, allowed_attrs=allowed_attrs, **kw)
             result.append('<%s%s%s>' % (tag, attributes, is_self_closing))
@@ -388,7 +388,7 @@
             if newline:
                 result.append(self._newline())
             result.append('</%s>' % (tag))
-            tagstr = ''.join(result)            
+            tagstr = ''.join(result)
         else:
             # Inline elements 
             # Pull from stack, ignore order, that is not our problem.
@@ -433,7 +433,7 @@
         if newline:
             result.append('\n')
         return ''.join(result)
-        
+
     def endContent(self, newline=True):
         """ Close page content div.
 
@@ -455,7 +455,7 @@
         result = []
         result.append(self.anchordef(aid))
         result.append(self._close('div', newline=newline))
-        return ''.join(result) 
+        return ''.join(result)
 
     def lang(self, on, lang_name):
         """ Insert text with specific lang and direction.
@@ -472,10 +472,10 @@
             return self._close(tag)
 
         # Direction did not change, no need for span
-        return ''            
-                
+        return ''
+
     # Links ##############################################################
-    
+
     def pagelink(self, on, pagename='', page=None, **kw):
         """ Link to a page.
 
@@ -486,7 +486,7 @@
         """
         apply(FormatterBase.pagelink, (self, on, pagename, page), kw)
         if page is None:
-            page = Page(self.request, pagename, formatter=self);
+            page = Page(self.request, pagename, formatter=self)
         if self.request.user.show_nonexist_qm and on and not page.exists():
             self.pagelink_preclosed = True
             return (page.link_to(self.request, on=1, **kw) +
@@ -565,7 +565,7 @@
 
             if css:
                 attrs['class'] = css
-            
+
             markup = self._open('a', attr=attrs, **kw)
         else:
             markup = self._close('a')
@@ -641,7 +641,7 @@
         return (self.url(1, target, css='attachment', title="attachment:%s" % url) +
                 self.text(text) +
                 self.url(0))
-    
+
     def attachment_image(self, url, **kw):
         _ = self.request.getText
         pagename, filename = AttachFile.absoluteName(url, self.page.page_name)
@@ -659,7 +659,7 @@
             title="attachment:%s" % url,
             src=AttachFile.getAttachUrl(pagename, filename, self.request, addts=1),
             css="attachment")
-    
+
     def attachment_drawing(self, url, text, **kw):
         _ = self.request.getText
         pagename, filename = AttachFile.absoluteName(url, self.page.page_name)
@@ -724,10 +724,10 @@
                                      self.image(alt=url,
                                                 src=AttachFile.getAttachUrl(pagename, filename, self.request, addts=1), css="drawing"),
                                      title="%s" % (_('Edit drawing %(filename)s') % {'filename': self.text(fname)}))
-        
-    
+
+
     # Text ##############################################################
-    
+
     def _text(self, text):
         text = wikiutil.escape(text)
         if self._in_code:
@@ -735,7 +735,7 @@
         return text
 
     # Inline ###########################################################
-        
+
     def strong(self, on, **kw):
         """Creates an HTML <strong> element.
 
@@ -826,11 +826,11 @@
         """
         tag = 'tt'
         # Maybe we don't need this, because we have tt will be in inlineStack.
-        self._in_code = on        
+        self._in_code = on
         if on:
             return self._open(tag, allowed_attrs=[], **kw)
         return self._close(tag)
-        
+
     def small(self, on, **kw):
         """Creates a <small> element for smaller font.
 
@@ -867,7 +867,7 @@
         if on:
             return self._open(tag, newline=1, **kw)
         return self._close(tag)
-                
+
     # Use by code area
     _toggleLineNumbersScript = """
 <script type="text/javascript">
@@ -920,7 +920,7 @@
 }
 </script>
 """
-    
+
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
         """Creates a formatted code region, with line numbering.
 
@@ -997,7 +997,7 @@
         return ['<span class="%s">' % tok_type, '</span>'][not on]
 
     # Paragraphs, Lines, Rules ###########################################
-    
+
     def _indent_spaces(self):
         """Returns space(s) for indenting the html source so list nesting is easy to read.
 
@@ -1023,7 +1023,7 @@
         if self._in_code_area:
             preformatted = 1
         return ['\n', '<br />\n'][not preformatted] + self._indent_spaces()
-        
+
     def paragraph(self, on, **kw):
         """Creates a paragraph with a <p> element.
         
@@ -1051,7 +1051,7 @@
             # Add hr class: hr1 - hr6
             return self._open('hr', newline=1, attr={'class': 'hr%d' % size}, **kw)
         return self._open('hr', newline=1, **kw)
-                
+
     def icon(self, type):
         return self.request.theme.make_icon(type)
 
@@ -1091,7 +1091,7 @@
         else:
             tagstr = self._close(tag, newline=1)
         return tagstr
-    
+
     def bullet_list(self, on, **kw):
         """Creates an HTML ordered list, <ul> element.
 
@@ -1117,7 +1117,7 @@
         """
         tag = 'li'
         if on:
-            tagstr =  self._open(tag, newline=1, **kw)
+            tagstr = self._open(tag, newline=1, **kw)
         else:
             tagstr = self._close(tag, newline=1)
         return tagstr
@@ -1185,7 +1185,7 @@
         # closing tag, with empty line after, to make source more readable
         if not on:
             return self._close('h%d' % heading_depth) + '\n'
-            
+
         # create section number
         number = ''
         if self._show_section_numbers:
@@ -1214,7 +1214,7 @@
                        self.url(0)))
         return "%s%s%s" % (result, kw.get('icons', ''), number)
 
-    
+
     # Tables #############################################################
 
     _allowed_table_attrs = {
@@ -1303,8 +1303,8 @@
             result.append(self._close('table'))
             result.append(self._close('div'))
 
-        return ''.join(result)    
-    
+        return ''.join(result)
+
     def table_row(self, on, attrs=None, **kw):
         tag = 'tr'
         if on:
@@ -1316,7 +1316,7 @@
                              allowed_attrs=self._allowed_table_attrs['row'],
                              **kw)
         return self._close(tag) + '\n'
-    
+
     def table_cell(self, on, attrs=None, **kw):
         tag = 'td'
         if on:
@@ -1349,7 +1349,7 @@
         if on:
             return self._open(tag, attr={'class': 'message'}, **kw)
         return self._close(tag)
-    
+
     def div(self, on, **kw):
         tag = 'div'
         if on:
@@ -1361,5 +1361,5 @@
         if on:
             return self._open(tag, **kw)
         return self._close(tag)
-    
 
+
--- a/MoinMoin/formatter/text_plain.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/text_plain.py	Mon Jul 24 22:18:49 2006 +0200
@@ -53,7 +53,7 @@
                 self._url = None
                 self._text = None
                 return u' [%s]' % (self._url)
-            
+
     def url(self, on, url='', css=None, **kw):
         if on:
             self._url = url
@@ -81,7 +81,7 @@
 
     def attachment_drawing(self, url, text, **kw):
         return "[drawing:%s]" % text
-    
+
     def text(self, text, **kw):
         self._did_para = 0
         if self._text is not None:
@@ -138,7 +138,7 @@
         else:
             self._did_para = 1
             return u'\n'
-        
+
     def sup(self, on, **kw):
         return u'^'
 
@@ -211,7 +211,7 @@
             self._text = []
             return '\n\n'
         else:
-            result =  u'\n%s\n\n' % (u'=' * len("".join(self._text)))
+            result = u'\n%s\n\n' % (u'=' * len("".join(self._text)))
             self._text = None
             return result
 
--- a/MoinMoin/formatter/text_python.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/text_python.py	Mon Jul 24 22:18:49 2006 +0200
@@ -21,7 +21,7 @@
     """
 
     defaultDependencies = ["time"]
-    
+
     def __init__(self, request, static=[], formatter=None, **kw):
         if formatter:
             self.formatter = formatter
@@ -71,7 +71,7 @@
             i += 1
         source.append(self.text_cmd_end)
         source.append(self.__adjust_formatter_state())
-        
+
         self.code_fragments = [] # clear code fragments to make
                                  # this object reusable
         return "".join(source)
@@ -99,7 +99,7 @@
             self.__lang = self.request.current_lang
             return 'request.current_lang = %r\n' % self.__lang
         return ''
-        
+
     def __adjust_formatter_state(self):
         result = self.__adjust_language_state()
         if self.__in_p != self.formatter.in_p:
@@ -109,11 +109,11 @@
         if self.__in_pre != self.formatter.in_pre:
             result = "%s%s.in_pre = %r\n" % (result, self.__formatter,
                                            self.formatter.in_pre)
-            self.__in_pre = self.formatter.in_pre        
+            self.__in_pre = self.formatter.in_pre
         return result
-    
+
     # Public methods ---------------------------------------------------
-        
+
     def pagelink(self, on, pagename='', page=None, **kw):
         if on:
             return self.__insert_code('page=Page(request, %r, formatter=%s);'
@@ -133,24 +133,24 @@
         return self.__insert_code(
             'request.write(%s.attachment_image(%r, **%r))' %
             (self.__formatter, url, kw))
-    
+
     def attachment_drawing(self, url, text, **kw):
         return self.__insert_code(
             'request.write(%s.attachment_drawing(%r, %r, **%r))' %
             (self.__formatter, url, text, kw))
-    
+
     def attachment_inlined(self, url, text, **kw):
         return self.__insert_code(
             'request.write(%s.attachment_inlined(%r, %r, **%r))' %
             (self.__formatter, url, text, kw))
 
-    def heading(self, on, depth, **kw):        
+    def heading(self, on, depth, **kw):
         if on:
             code = [
                 self.__adjust_language_state(),
                 'request.write(%s.heading(%r, %r, **%r))' % (self.__formatter,
                                                              on, depth, kw),
-                ]     
+                ]
             return self.__insert_code(''.join(code))
         else:
             return self.formatter.heading(on, depth, **kw)
@@ -172,7 +172,7 @@
                 '%srequest.write(%s.macro(macro_obj, %r, %r))' %
                 (self.__adjust_formatter_state(),
                  self.__formatter, name, args))
-            
+
     def parser(self, parser_name, lines):
         """ parser_name MUST be valid!
             prints out the result instead of returning it!
--- a/MoinMoin/formatter/text_xml.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/formatter/text_xml.py	Mon Jul 24 22:18:49 2006 +0200
@@ -175,11 +175,11 @@
         return '<anchor id="%s"/>' % id
 
     def anchorlink(self, on, name='', **kw):
-        id = kw.get('id',None)
+        id = kw.get('id', None)
         extra = ''
         if id:
             extra = ' id="%s"' % id
-        return ('<link anchor="%s"%s>' % (name, extra) ,'</link>') [not on]
+        return ('<link anchor="%s"%s>' % (name, extra), '</link>') [not on]
 
     def underline(self, on, **kw):
         return self.strong(on) # no underline in StyleBook
--- a/MoinMoin/logfile/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/logfile/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -67,7 +67,7 @@
         while i < length:
             result = self.offsets[i-1] + tmp
             tmp = self.offsets[i]
-            self.offsets[i] =  result
+            self.offsets[i] = result
             i = i + 1
         self.offsets[0] = offset
 
@@ -79,7 +79,7 @@
     Overwrite .parser() and .add() to customize this class to
     special log files
     """
-    
+
     def __init__(self, filename, buffer_size=65536):
         """
         @param filename: name of the log file
@@ -106,7 +106,7 @@
             except StopIteration:
                 return
             yield result
-            
+
     def sanityCheck(self):
         """ Check for log file write access.
         
@@ -122,7 +122,7 @@
         """
         generate some attributes when needed
         """
-        if name=="_LogFile__rel_index":
+        if name == "_LogFile__rel_index":
             # starting iteration from begin
             self.__buffer1 = LineBuffer(self._input, 0, self.buffer_size)
             self.__buffer2 = LineBuffer(self._input,
@@ -164,7 +164,7 @@
             return os.path.getsize(self.__filename)
         except OSError, err:
             if err.errno == errno.ENOENT:
-                return 0            
+                return 0
             raise
 
     def lines(self):
@@ -194,7 +194,7 @@
     def date(self):
         """ Return timestamp of log file in usecs """
         try:
-            mtime = os.path.getmtime(self.__filename)            
+            mtime = os.path.getmtime(self.__filename)
         except OSError, err:
             if err.errno == errno.ENOENT:
                 # This can happen on fresh wiki when building the index
@@ -237,7 +237,7 @@
                     self.__rel_index = (self.__rel_index +
                                         self.__buffer1.len)
                     self.__buffer = self.__buffer1
-                
+
         while self.__rel_index >= self.__buffer.len:
             if self.__buffer == self.__buffer1:
                 # change to buffer 2
@@ -248,7 +248,7 @@
                 tmpbuff = LineBuffer(self._input,
                                      self.__buffer1.offsets[-1],
                                      self.buffer_size)
-                if tmpbuff.len==0:
+                if tmpbuff.len == 0:
                     # end of file
                     if self.__lineno:
                         self.__lineno = (self.__lineno + lines -
@@ -258,7 +258,7 @@
                     return True
                 # shift buffers
                 self.__buffer1 = self.__buffer2
-                self.__buffer2 = tmpbuff                
+                self.__buffer2 = tmpbuff
                 self.__rel_index = self.__rel_index - self.__buffer1.len
         if self.__lineno: self.__lineno += lines
         return False
@@ -278,15 +278,16 @@
         XXX It does not raise anything!
         """
         result = None
-        while result == None:
-            while result == None:
+        while result is None:
+            while result is None:
                 result = self.__next()
             if self.filter and not self.filter(result):
                 result = None
         return result
-    
+
     def __previous(self):
-        if self.peek(-1): raise StopIteration
+        if self.peek(-1):
+            raise StopIteration
         return self.parser(self.__buffer.lines[self.__rel_index])
 
     def previous(self):
@@ -295,8 +296,8 @@
         raises StopIteration at file begin
         """
         result = None
-        while result == None:
-            while result == None:
+        while result is None:
+            while result is None:
                 result = self.__previous()
             if self.filter and not self.filter(result):
                 result = None
@@ -319,16 +320,16 @@
         """moves file position to the end"""
         self._input.seek(0, 2) # to end of file
         size = self._input.tell()
-        if (not self.__buffer2) or (size>self.__buffer2.offsets[-1]):
+        if (not self.__buffer2) or (size > self.__buffer2.offsets[-1]):
             self.__buffer2 = LineBuffer(self._input,
                                         size,
                                         self.buffer_size,
-                                        forward = False)
-            
+                                        forward=False)
+
             self.__buffer1 = LineBuffer(self._input,
                                         self.__buffer2.offsets[0],
                                         self.buffer_size,
-                                        forward = False)
+                                        forward=False)
         self.__buffer = self.__buffer2
         self.__rel_index = self.__buffer2.len
         self.__lineno = None
@@ -341,7 +342,7 @@
         For this plain file implementation position is an Integer.
         """
         return self.__buffer.offsets[self.__rel_index]
-        
+
     def seek(self, position, line_no=None):
         """ moves file position to an value formerly gotten from .position().
         To enable line counting line_no must be provided.
@@ -362,7 +363,7 @@
             self.__buffer1 = LineBuffer(self._input,
                                         position,
                                         self.buffer_size,
-                                        forward = False)
+                                        forward=False)
             self.__buffer2 = LineBuffer(self._input,
                                         position,
                                         self.buffer_size)
@@ -374,7 +375,7 @@
     def line_no(self):
         """@return: the current line number or None if line number is unknown"""
         return self.__lineno
-    
+
     def calculate_line_no(self):
         """ Calculate the current line number from buffer offsets
         
@@ -404,14 +405,14 @@
         """
         line = "\t".join(data)
         self._add(line)
-        
+
     def _add(self, line):
         """
         @param line: flat line
         @type line: String
         write on entry in the log file
         """
-        if line != None:
+        if line is not None:
             if line[-1] != '\n':
                 line += '\n'
             self._output.write(line)
--- a/MoinMoin/logfile/editlog.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/logfile/editlog.py	Mon Jul 24 22:18:49 2006 +0200
@@ -34,8 +34,9 @@
 
     def is_from_current_user(self, request):
         user = request.user
-        if user.id: return user.id==self.userid
-        return request.remote_addr==self.addr        
+        if user.id:
+            return user.id == self.userid
+        return request.remote_addr == self.addr
 
     def getEditorData(self, request):
         """ Return a tuple of type id and string or Page object
@@ -112,7 +113,7 @@
                     request.formatter.url(0))
         elif kind == 'ip':
             idx = info.find('.')
-            if idx==-1:
+            if idx == -1:
                 idx = len(info)
             title = wikiutil.escape('???' + title)
             text = wikiutil.escape(info[:idx])
@@ -124,7 +125,7 @@
 class EditLog(LogFile):
 
     def __init__(self, request, filename=None, buffer_size=65536, **kw):
-        if filename == None:
+        if filename is None:
             rootpagename = kw.get('rootpagename', None)
             if rootpagename:
                 filename = Page(request, rootpagename).getPagePath('edit-log', isfile=1)
@@ -133,7 +134,7 @@
         LogFile.__init__(self, filename, buffer_size)
         self._NUM_FIELDS = 9
         self._usercache = {}
-        
+
         # Used by antispam in order to show an internal name instead
         # of a confusing userid
         self.uid_override = kw.get('uid_override', None)
@@ -155,16 +156,16 @@
                     hostname = host
             else:
                 hostname = host
-            
-            remap_chars = {u'\t': u' ', u'\r': u' ', u'\n': u' ',}
+
+            remap_chars = {u'\t': u' ', u'\r': u' ', u'\n': u' ', }
             comment = comment.translate(remap_chars)
             user_id = request.user.valid and request.user.id or ''
 
-            if self.uid_override != None:
+            if self.uid_override is not None:
                 user_id = ''
                 hostname = self.uid_override
                 host = ''
-            
+
             line = u"\t".join((str(long(mtime)), # has to be long for py 2.2.x
                                "%08d" % rev,
                                action,
@@ -193,16 +194,16 @@
         result.pagename = wikiutil.unquoteWikiname(result.pagename.encode('ascii'))
         result.ed_time_usecs = long(result.ed_time_usecs or '0') # has to be long for py 2.2.x
         return result
-    
+
     def set_filter(self, **kw):
         expr = "1"
         for field in ['pagename', 'addr', 'hostname', 'userid']:
             if kw.has_key(field):
                 expr = "%s and x.%s == %s" % (expr, field, `kw[field]`)
-                
+
         if kw.has_key('ed_time_usecs'):
             expr = "%s and long(x.ed_time_usecs) == %s" % (expr, long(kw['ed_time_usecs'])) # must be long for py 2.2.x
 
         self.filter = eval("lambda x: " + expr)
-    
 
+
--- a/MoinMoin/logfile/eventlog.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/logfile/eventlog.py	Mon Jul 24 22:18:49 2006 +0200
@@ -10,9 +10,9 @@
 from MoinMoin.util import web
 
 class EventLog(LogFile):
-    
+
     def __init__(self, request, filename=None, buffer_size=65536, **kw):
-        if filename == None:
+        if filename is None:
             rootpagename = kw.get('rootpagename', None)
             if rootpagename:
                 from MoinMoin.Page import Page
@@ -28,7 +28,7 @@
         """
         if request.isSpiderAgent:
             return
-        
+
         if mtime_usecs is None:
             mtime_usecs = wikiutil.timestamp2version(time.time())
 
@@ -52,9 +52,9 @@
             # badly formatted line in file, skip it
             return None
         return long(time_usecs), eventtype, wikiutil.parseQueryString(kvpairs)
-                                                                        
-    def set_filter(self, event_types = None):
-        if event_types == None:
+
+    def set_filter(self, event_types=None):
+        if event_types is None:
             self.filter = None
         else:
             self.filter = lambda line: (line[1] in event_types)
--- a/MoinMoin/macro/EditedSystemPages.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/macro/EditedSystemPages.py	Mon Jul 24 22:18:49 2006 +0200
@@ -44,7 +44,7 @@
             result.append(f.pagelink(0, name))
             result.append(f.listitem(0))
         result.append(f.number_list(0))
- 
+
         return ''.join(result)
 
 
--- a/MoinMoin/macro/Include.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/macro/Include.py	Mon Jul 24 22:18:49 2006 +0200
@@ -45,7 +45,7 @@
         h = title.strip()
         level = 1
         while h[level:level+1] == '=': level = level+1
-        depth = min(5,level)
+        depth = min(5, level)
         title_text = h[level:-level].strip()
         titles.append((title_text, level))
     return titles
@@ -65,7 +65,7 @@
 
     # prepare including page
     result = []
-    print_mode = macro.form.has_key('action') and macro.form['action'][0] in ("print", "format")
+    print_mode = request.action in ("print", "format")
     this_page = macro.formatter.page
     if not hasattr(this_page, '_macroInclude_pagelist'):
         this_page._macroInclude_pagelist = {}
@@ -177,7 +177,7 @@
 
         if not hasattr(request, "_Include_backto"):
             request._Include_backto = this_page.page_name
-        
+
         # do headings
         level = None
         if args.group('heading') and args.group('hquote'):
--- a/MoinMoin/macro/MonthCalendar.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/macro/MonthCalendar.py	Mon Jul 24 22:18:49 2006 +0200
@@ -240,7 +240,7 @@
 _arg_anniversary = r',\s*(?P<anniversary>[+-]?\d+)?\s*'
 _arg_template = r',\s*(?P<template>[^, ]+)?\s*' # XXX see basepage comment
 _args_re_pattern = r'^(%s)?(%s)?(%s)?(%s)?(%s)?(%s)?(%s)?(%s)?$' % \
-                     (_arg_basepage, _arg_year, _arg_month, \
+                     (_arg_basepage, _arg_year, _arg_month,
                       _arg_offset, _arg_offset2, _arg_height6, _arg_anniversary, _arg_template)
 
 
--- a/MoinMoin/macro/Navigation.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/macro/Navigation.py	Mon Jul 24 22:18:49 2006 +0200
@@ -74,8 +74,7 @@
         self._ = self.macro.request.getText
 
         self.pagename = self.macro.formatter.page.page_name
-        self.print_mode = self.macro.request.form.has_key('action') \
-            and self.macro.request.form['action'][0] == 'print'
+        self.print_mode = self.macro.request.action == 'print'
         self.media = self.macro.request.form.get('media', [None])[0]
         self.querystr = self.print_mode and self.PROJECTION or ''
 
--- a/MoinMoin/macro/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/macro/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -66,20 +66,20 @@
     defaultDependency = ["time"]
 
     Dependencies = {
-        "TitleSearch" : ["namespace"],
-        "Goto"        : [],
-        "WordIndex"   : ["namespace"],
-        "TitleIndex"  : ["namespace"],
-        "InterWiki"   : ["pages"],  # if interwikimap is editable
-        "PageCount"   : ["namespace"],
-        "Icon"        : ["user"], # users have different themes and user prefs
-        "PageList"    : ["namespace"],
-        "Date"        : ["time"],
-        "DateTime"    : ["time"],
-        "UserPreferences" :["time"],
-        "Anchor"      : [],
-        "Mailto"      : ["user"],
-        "GetVal"      : ["pages"],
+        "TitleSearch": ["namespace"],
+        "Goto": [],
+        "WordIndex": ["namespace"],
+        "TitleIndex": ["namespace"],
+        "InterWiki": ["pages"],  # if interwikimap is editable
+        "PageCount": ["namespace"],
+        "Icon": ["user"], # users have different themes and user prefs
+        "PageList": ["namespace"],
+        "Date": ["time"],
+        "DateTime": ["time"],
+        "UserPreferences": ["time"],
+        "Anchor": [],
+        "Mailto": ["user"],
+        "GetVal": ["pages"],
         "TemplateList": ["namespace"],
         }
 
--- a/MoinMoin/multiconfig.py	Sat Jul 22 13:38:15 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,716 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - Multiple configuration handler and Configuration defaults class
-
-    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
-                2005-2006 by MoinMoin:ThomasWaldmann.
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import re, os, sys
-from MoinMoin import error
-import MoinMoin.auth as authmodule
-
-_url_re_cache = None
-_farmconfig_mtime = None
-_config_cache = {}
-
-
-def _importConfigModule(name):
-    """ Import and return configuration module and its modification time
-    
-    Handle all errors except ImportError, because missing file is not
-    always an error.
-    
-    @param name: module name
-    @rtype: tuple
-    @return: module, modification time
-    """
-    try:
-        module = __import__(name, globals(), {})
-        mtime = os.path.getmtime(module.__file__)
-    except ImportError:
-        raise
-    except IndentationError, err:
-        msg = '''IndentationError: %(err)s
-
-The configuration files are python modules. Therefore, whitespace is
-important. Make sure that you use only spaces, no tabs are allowed here!
-You have to use four spaces at the beginning of the line mostly.
-''' % {
-    'err': err,
-}
-        raise error.ConfigurationError(msg)
-    except Exception, err:
-        msg = '%s: %s' % (err.__class__.__name__, str(err))
-        raise error.ConfigurationError(msg)
-    return module, mtime
-
-
-def _url_re_list():
-    """ Return url matching regular expression
-
-    Import wikis list from farmconfig on the first call and compile the
-    regexes. Later then return the cached regex list.
-
-    @rtype: list of tuples of (name, compiled re object)
-    @return: url to wiki config name matching list
-    """
-    global _url_re_cache, _farmconfig_mtime
-    if _url_re_cache is None:
-        try:
-            farmconfig, _farmconfig_mtime = _importConfigModule('farmconfig')
-        except ImportError:
-            # Default to wikiconfig for all urls.
-            _farmconfig_mtime = 0
-            _url_re_cache = [('wikiconfig', re.compile(r'.')), ] # matches everything
-        else:
-            try:
-                cache = []
-                for name, regex in farmconfig.wikis:
-                    cache.append((name, re.compile(regex)))
-                _url_re_cache = cache
-            except AttributeError:
-                msg = """
-Missing required 'wikis' list in 'farmconfig.py'.
-
-If you run a single wiki you do not need farmconfig.py. Delete it and
-use wikiconfig.py.
-"""
-                raise error.ConfigurationError(msg)
-    return _url_re_cache
-
-
-def _makeConfig(name):
-    """ Create and return a config instance 
-
-    Timestamp config with either module mtime or farmconfig mtime. This
-    mtime can be used later to invalidate older caches.
-
-    @param name: module name
-    @rtype: DefaultConfig sub class instance
-    @return: new configuration instance
-    """
-    global _farmconfig_mtime
-    try:
-        module, mtime = _importConfigModule(name)
-        configClass = getattr(module, 'Config')
-        cfg = configClass(name)
-        cfg.cfg_mtime = max(mtime, _farmconfig_mtime)
-    except ImportError, err:
-        msg = '''ImportError: %(err)s
-
-Check that the file is in the same directory as the server script. If
-it is not, you must add the path of the directory where the file is
-located to the python path in the server script. See the comments at
-the top of the server script.
-
-Check that the configuration file name is either "wikiconfig.py" or the
-module name specified in the wikis list in farmconfig.py. Note that the
-module name does not include the ".py" suffix.
-''' % {
-    'err': err,
-}
-        raise error.ConfigurationError(msg)
-    except AttributeError, err:
-        msg = '''AttributeError: %(err)s
-
-Could not find required "Config" class in "%(name)s.py".
-
-This might happen if you are trying to use a pre 1.3 configuration file, or
-made a syntax or spelling error.
-
-Another reason for this could be a name clash. It is not possible to have
-config names like e.g. stats.py - because that colides with MoinMoin/stats/ -
-have a look into your MoinMoin code directory what other names are NOT
-possible.
-
-Please check your configuration file. As an example for correct syntax,
-use the wikiconfig.py file from the distribution.
-''' % {
-    'name': name,
-    'err': err,
-}
-        raise error.ConfigurationError(msg)
-    return cfg
-
-
-def _getConfigName(url):
-    """ Return config name for url or raise """
-    for name, regex in _url_re_list():
-        match = regex.match(url)
-        if match:
-            return name
-    # nothing matched
-    msg = '''
-Could not find a match for url: "%(url)s".
-
-Check your URL regular expressions in the "wikis" list in
-"farmconfig.py". 
-''' % {
-    'url': url,
-}
-    raise error.ConfigurationError(msg)
-
-
-def getConfig(url):
-    """ Return cached config instance for url or create new one
-
-    If called by many threads in the same time multiple config
-    instances might be created. The first created item will be
-    returned, using dict.setdefault.
-
-    @param url: the url from request, possibly matching specific wiki
-    @rtype: DefaultConfig subclass instance
-    @return: config object for specific wiki
-    """
-    configName = _getConfigName(url)
-    try:
-        config = _config_cache[configName]
-    except KeyError:
-        config = _makeConfig(configName)
-        config = _config_cache.setdefault(configName, config)
-    return config
-
-
-# This is a way to mark some text for the gettext tools so that they don't
-# get orphaned. See http://www.python.org/doc/current/lib/node278.html.
-def _(text): return text
-
-
-class DefaultConfig:
-    """ default config values """
-
-    # All acl_rights_* lines must use unicode!
-    acl_rights_default = u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write"
-    acl_rights_before = u""
-    acl_rights_after = u""
-    acl_rights_valid = ['read', 'write', 'delete', 'revert', 'admin']
-
-    actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage', 'test', ]
-    allow_xslt = 0
-    attachments = None # {'dir': path, 'url': url-prefix}
-    auth = [authmodule.moin_login, authmodule.moin_session, ]
-
-    backup_compression = 'gz'
-    backup_users = []
-    backup_include = []
-    backup_exclude = [
-        r"(.+\.py(c|o)$)",
-        r"%(cache_dir)s",
-        r"%(/)spages%(/)s.+%(/)scache%(/)s[^%(/)s]+$" % {'/': os.sep},
-        r"%(/)s(edit-lock|event-log|\.DS_Store)$" % {'/': os.sep},
-        ]
-    backup_storage_dir = '/tmp'
-    backup_restore_target_dir = '/tmp'
-
-    bang_meta = 1
-    caching_formats = ['text_html']
-    changed_time_fmt = '%H:%M'
-
-    # chars_{upper,lower,digits,spaces} see MoinMoin/util/chartypes.py
-
-    # if you have gdchart, add something like
-    # chart_options = {'width = 720, 'height': 540}
-    chart_options = None
-
-    config_check_enabled = 0
-
-    cookie_domain = None # use '.domain.tld" for a farm with hosts in that domain
-    cookie_path = None   # use '/wikifarm" for a farm with pathes below that path
-    cookie_lifetime = 12 # 12 hours from now
-    cookie_secret = '1234' # secret value for crypting session cookie - you should change this :)
-
-    data_dir = './data/'
-    data_underlay_dir = './underlay/'
-
-    date_fmt = '%Y-%m-%d'
-    datetime_fmt = '%Y-%m-%d %H:%M:%S'
-
-    default_markup = 'wiki'
-    docbook_html_dir = r"/usr/share/xml/docbook/stylesheet/nwalsh/html/" # correct for debian sarge
-
-    editor_default = 'text' # which editor is called when nothing is specified
-    editor_ui = 'freechoice' # which editor links are shown on user interface
-    editor_force = False
-    editor_quickhelp = {# editor markup hints quickhelp 
-        'wiki': _("""\
- Emphasis:: [[Verbatim('')]]''italics''[[Verbatim('')]]; [[Verbatim(''')]]'''bold'''[[Verbatim(''')]]; [[Verbatim(''''')]]'''''bold italics'''''[[Verbatim(''''')]]; [[Verbatim('')]]''mixed ''[[Verbatim(''')]]'''''bold'''[[Verbatim(''')]] and italics''[[Verbatim('')]]; [[Verbatim(----)]] horizontal rule.
- Headings:: [[Verbatim(=)]] Title 1 [[Verbatim(=)]]; [[Verbatim(==)]] Title 2 [[Verbatim(==)]]; [[Verbatim(===)]] Title 3 [[Verbatim(===)]];   [[Verbatim(====)]] Title 4 [[Verbatim(====)]]; [[Verbatim(=====)]] Title 5 [[Verbatim(=====)]].
- Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1.#n start numbering at n; space alone indents.
- Links:: [[Verbatim(JoinCapitalizedWords)]]; [[Verbatim(["brackets and double quotes"])]]; url; [url]; [url label].
- Tables:: || cell text |||| cell text spanning 2 columns ||;    no trailing white space allowed after tables or titles.
-
-(!) For more help, see HelpOnEditing or SyntaxReference.
-"""),
-        'rst': _("""\
-Emphasis: <i>*italic*</i> <b>**bold**</b> ``monospace``<br/>
-<br/><pre>
-Headings: Heading 1  Heading 2  Heading 3
-          =========  ---------  ~~~~~~~~~
-
-Horizontal rule: ---- 
-Links: TrailingUnderscore_ `multi word with backticks`_ external_ 
-
-.. _external: http://external-site.net/foo/
-
-Lists: * bullets; 1., a. numbered items.
-</pre>
-<br/>
-(!) For more help, see the 
-<a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">
-reStructuredText Quick Reference
-</a>.
-"""),
-    }
-    edit_locking = 'warn 10' # None, 'warn <timeout mins>', 'lock <timeout mins>'
-    edit_rows = 20
-
-    hacks = {} # { 'feature1': value1, ... }
-               # Configuration for features still in development.
-               # For boolean stuff just use config like this:
-               #   hacks = { 'feature': True, ...}
-               # and in the code use:
-               #   if cfg.hacks.get('feature', False): <doit>
-               # A non-existing hack key should ever mean False, None, "", [] or {}!
-
-    hosts_deny = []
-
-    html_head = ''
-    html_head_queries = '''<meta name="robots" content="noindex,nofollow">\n'''
-    html_head_posts   = '''<meta name="robots" content="noindex,nofollow">\n'''
-    html_head_index   = '''<meta name="robots" content="index,follow">\n'''
-    html_head_normal  = '''<meta name="robots" content="index,nofollow">\n'''
-    html_pagetitle = None
-
-    interwiki_preferred = [] # list of wiki names to show at top of interwiki list
-
-    language_default = 'en'
-    language_ignore_browser = False # ignore browser settings, use language_default
-                                    # or user prefs
-
-    log_reverse_dns_lookups = True  # if we do reverse dns lookups for logging hostnames
-                                    # instead of just IPs
-
-    xapian_search = False # disabled until xapian is finished
-    xapian_index_dir = None
-    xapian_stemming = True
-
-    mail_login = None # or "user pwd" if you need to use SMTP AUTH
-    mail_sendmail = None # "/usr/sbin/sendmail -t -i" to not use SMTP, but sendmail
-    mail_smarthost = None
-    mail_from = None # u'Juergen Wiki <noreply@jhwiki.org>'
-
-    mail_import_subpage_template = u"$from-$date-$subject" # used for mail import
-    mail_import_wiki_address = None # the e-mail address for e-mails that should go into the wiki
-    mail_import_secret = ""
-
-    navi_bar = [u'RecentChanges', u'FindPage', u'HelpContents', ]
-    nonexist_qm = 0
-
-    page_credits = [
-        '<a href="http://moinmoin.wikiwikiweb.de/">MoinMoin Powered</a>',
-        '<a href="http://www.python.org/">Python Powered</a>',
-        '<a href="http://validator.w3.org/check?uri=referer">Valid HTML 4.01</a>',
-        ]
-    page_footer1 = ''
-    page_footer2 = ''
-
-    page_header1 = ''
-    page_header2 = ''
-
-    page_front_page = u'HelpOnLanguages' # this will make people choose a sane config
-    page_local_spelling_words = u'LocalSpellingWords'
-    page_category_regex = u'^Category[A-Z]'
-    page_dict_regex = u'[a-z0-9]Dict$'
-    page_group_regex = u'[a-z0-9]Group$'
-    page_template_regex = u'[a-z0-9]Template$'
-
-    page_license_enabled = 0
-    page_license_page = u'WikiLicense'
-
-    # These icons will show in this order in the iconbar, unless they
-    # are not relevant, e.g email icon when the wiki is not configured
-    # for email.
-    page_iconbar = ["up", "edit", "view", "diff", "info", "subscribe", "raw", "print", ]
-
-    # Standard buttons in the iconbar
-    page_icons_table = {
-        # key           last part of url, title, icon-key
-        'help':        ("%(q_page_help_contents)s", "%(page_help_contents)s", "help"),
-        'find':        ("%(q_page_find_page)s?value=%(q_page_name)s", "%(page_find_page)s", "find"),
-        'diff':        ("%(q_page_name)s?action=diff", _("Diffs"), "diff"),
-        'info':        ("%(q_page_name)s?action=info", _("Info"), "info"),
-        'edit':        ("%(q_page_name)s?action=edit", _("Edit"), "edit"),
-        'unsubscribe': ("%(q_page_name)s?action=subscribe", _("UnSubscribe"), "unsubscribe"),
-        'subscribe':   ("%(q_page_name)s?action=subscribe", _("Subscribe"), "subscribe"),
-        'raw':         ("%(q_page_name)s?action=raw", _("Raw"), "raw"),
-        'xml':         ("%(q_page_name)s?action=show&amp;mimetype=text/xml", _("XML"), "xml"),
-        'print':       ("%(q_page_name)s?action=print", _("Print"), "print"),
-        'view':        ("%(q_page_name)s", _("View"), "view"),
-        'up':          ("%(q_page_parent_page)s", _("Up"), "up"),
-        }
-
-    refresh = None # (minimum_delay, type), e.g.: (2, 'internal')
-    rss_cache = 60 # suggested caching time for RecentChanges RSS, in seconds
-    shared_intermap = None # can be string or list of strings (filenames)
-    show_hosts = 1
-    show_interwiki = 0
-    show_login = 1
-    show_names = True
-    show_section_numbers = 0
-    show_timings = 0
-    show_version = 0
-    siteid = 'default'
-    stylesheets = [] # list of tuples (media, csshref) to insert after theme css, before user css
-    superuser = [] # list of unicode user names that have super powers :)
-
-    surge_action_limits = {# allow max. <count> <action> requests per <dt> secs
-        # action: (count, dt)
-        'all': (30, 30),
-        'show': (30, 60),
-        'recall': (5, 60),
-        'raw': (20, 40),  # some people use this for css
-        'AttachFile': (90, 60),
-        'diff': (30, 60),
-        'fullsearch': (5, 60),
-        'edit': (10, 120),
-        'rss_rc': (1, 60),
-        'default': (30, 60),
-    }
-    surge_lockout_time = 3600 # secs you get locked out when you ignore warnings
-
-    theme_default = 'modern'
-    theme_force = False
-
-    trail_size = 5
-    tz_offset = 0.0 # default time zone offset in hours from UTC
-
-    user_autocreate = False # do we auto-create user profiles
-    user_email_unique = True # do we check whether a user's email is unique?
-
-    # a regex of HTTP_USER_AGENTS that should be excluded from logging
-    # and receive a FORBIDDEN for anything except viewing a page
-    ua_spiders = ('archiver|cfetch|crawler|curl|gigabot|googlebot|holmes|htdig|httrack|httpunit|jeeves|larbin|leech|'
-                  'linkbot|linkmap|linkwalk|mercator|mirror|msnbot|nutbot|omniexplorer|puf|robot|scooter|'
-                  'sherlock|slurp|sitecheck|spider|teleport|voyager|webreaper|wget')
-
-    # Wiki identity
-    sitename = u'Untitled Wiki'
-    url_prefix = '/wiki'
-    logo_string = None
-    interwikiname = None
-
-    url_mappings = {}
-
-    user_checkbox_fields = [
-        ('mailto_author', lambda _: _('Publish my email (not my wiki homepage) in author info')),
-        ('edit_on_doubleclick', lambda _: _('Open editor on double click')),
-        ('remember_last_visit', lambda _: _('After login, jump to last visited page')),
-        ('show_nonexist_qm', lambda _: _('Show question mark for non-existing pagelinks')),
-        ('show_page_trail', lambda _: _('Show page trail')),
-        ('show_toolbar', lambda _: _('Show icon toolbar')),
-        ('show_topbottom', lambda _: _('Show top/bottom links in headings')),
-        ('show_fancy_diff', lambda _: _('Show fancy diffs')),
-        ('wikiname_add_spaces', lambda _: _('Add spaces to displayed wiki names')),
-        ('remember_me', lambda _: _('Remember login information')),
-        ('want_trivial', lambda _: _('Subscribe to trivial changes')),
-
-        ('disabled', lambda _: _('Disable this account forever')),
-        # if an account is disabled, it may be used for looking up
-        # id -> username for page info and recent changes, but it
-        # is not usable for the user any more:
-    ]
-
-    user_checkbox_defaults = {'mailto_author':       0,
-                              'edit_on_doubleclick': 0,
-                              'remember_last_visit': 0,
-                              'show_nonexist_qm':    nonexist_qm,
-                              'show_page_trail':     1,
-                              'show_toolbar':        1,
-                              'show_topbottom':      0,
-                              'show_fancy_diff':     1,
-                              'wikiname_add_spaces': 0,
-                              'remember_me':         1,
-                              'want_trivial':        0,
-                             }
-
-    # don't let the user change those
-    # user_checkbox_disable = ['disabled', 'want_trivial']
-    user_checkbox_disable = []
-
-    # remove those checkboxes:
-    #user_checkbox_remove = ['edit_on_doubleclick', 'show_nonexist_qm', 'show_toolbar', 'show_topbottom',
-    #                        'show_fancy_diff', 'wikiname_add_spaces', 'remember_me', 'disabled',]
-    user_checkbox_remove = []
-
-    user_form_fields = [
-        ('name', _('Name'), "text", "36", _("(Use Firstname''''''Lastname)")),
-        ('aliasname', _('Alias-Name'), "text", "36", ''),
-        ('password', _('Password'), "password", "36", ''),
-        ('password2', _('Password repeat'), "password", "36", _('(Only for password change or new account)')),
-        ('email', _('Email'), "text", "36", ''),
-        ('css_url', _('User CSS URL'), "text", "40", _('(Leave it empty for disabling user CSS)')),
-        ('edit_rows', _('Editor size'), "text", "3", ''),
-    ]
-
-    user_form_defaults = {# key: default - do NOT remove keys from here!
-        'name': '',
-        'aliasname': '',
-        'password': '',
-        'password2': '',
-        'email': '',
-        'css_url': '',
-        'edit_rows': "20",
-    }
-
-    # don't let the user change those, but show them:
-    #user_form_disable = ['name', 'aliasname', 'email',]
-    user_form_disable = []
-
-    # remove those completely:
-    #user_form_remove = ['password', 'password2', 'css_url', 'logout', 'create', 'account_sendmail',]
-    user_form_remove = []
-
-    # attributes we do NOT save to the userpref file
-    user_transient_fields = ['id', 'valid', 'may', 'auth_username', 'trusted', 'password', 'password2', 'auth_method', 'auth_attribs', ]
-
-    user_homewiki = 'Self' # interwiki name for where user homepages are located
-
-    unzip_single_file_size = 2.0 * 1000**2
-    unzip_attachments_space = 200.0 * 1000**2
-    unzip_attachments_count = 51 # 1 zip file + 50 files contained in it
-
-    xmlrpc_putpage_enabled = 0 # if 0, putpage will write to a test page only
-    xmlrpc_putpage_trusted_only = 1 # if 1, you will need to be http auth authenticated
-
-    SecurityPolicy = None
-
-    def __init__(self, siteid):
-        """ Init Config instance """
-        self.siteid = siteid
-        if self.config_check_enabled:
-            self._config_check()
-
-        # define directories
-        self.moinmoin_dir = os.path.abspath(os.path.dirname(__file__))
-        data_dir = os.path.normpath(self.data_dir)
-        self.data_dir = data_dir
-        for dirname in ('user', 'cache', 'plugin'):
-            name = dirname + '_dir'
-            if not getattr(self, name, None):
-                setattr(self, name, os.path.join(data_dir, dirname))
-
-        # Try to decode certain names which allow unicode
-        self._decode()
-
-        self._check_directories()
-
-        if not isinstance(self.superuser, list):
-            msg = """The superuser setting in your wiki configuration is not a list
-                     (e.g. ['Sample User', 'AnotherUser']).
-                     Please change it in your wiki configuration and try again."""
-            raise error.ConfigurationError(msg)
-
-        self._loadPluginModule()
-
-        # Preparse user dicts
-        self._fillDicts()
-
-        # Normalize values
-        self.language_default = self.language_default.lower()
-
-        # Use site name as default name-logo
-        if self.logo_string is None:
-            self.logo_string = self.sitename
-
-        # Check for needed modules
-
-        # FIXME: maybe we should do this check later, just before a
-        # chart is needed, maybe in the chart module, instead doing it
-        # for each request. But this require a large refactoring of
-        # current code.
-        if self.chart_options:
-            try:
-                import gdchart
-            except ImportError:
-                self.chart_options = None
-
-        # post process
-        # we replace any string placeholders with config values
-        # e.g u'%(page_front_page)s' % self
-        self.navi_bar = [elem % self for elem in self.navi_bar]
-        self.backup_exclude = [elem % self for elem in self.backup_exclude]
-
-        # list to cache xapian searcher objects
-        self.xapian_searchers = []
-
-        # check if mail is possible and set flag:
-        self.mail_enabled = (self.mail_smarthost is not None or self.mail_sendmail is not None) and self.mail_from
-
-    def _config_check(self):
-        """ Check namespace and warn about unknown names
-        
-        Warn about names which are not used by DefaultConfig, except
-        modules, classes, _private or __magic__ names.
-
-        This check is disabled by default, when enabled, it will show an
-        error message with unknown names.
-        """
-        unknown = ['"%s"' % name for name in dir(self)
-                  if not name.startswith('_') and
-                  not DefaultConfig.__dict__.has_key(name) and
-                  not isinstance(getattr(self, name), (type(sys), type(DefaultConfig)))]
-        if unknown:
-            msg = """
-Unknown configuration options: %s.
-
-For more information, visit HelpOnConfiguration. Please check your
-configuration for typos before requesting support or reporting a bug.
-""" % ', '.join(unknown)
-            raise error.ConfigurationError(msg)
-
-    def _decode(self):
-        """ Try to decode certain names, ignore unicode values
-        
-        Try to decode str using utf-8. If the decode fail, raise FatalError. 
-
-        Certain config variables should contain unicode values, and
-        should be defined with u'text' syntax. Python decode these if
-        the file have a 'coding' line.
-        
-        This will allow utf-8 users to use simple strings using, without
-        using u'string'. Other users will have to use u'string' for
-        these names, because we don't know what is the charset of the
-        config files.
-        """
-        charset = 'utf-8'
-        message = u'''
-"%(name)s" configuration variable is a string, but should be
-unicode. Use %(name)s = u"value" syntax for unicode variables.
-
-Also check your "-*- coding -*-" line at the top of your configuration
-file. It should match the actual charset of the configuration file.
-'''
-
-        decode_names = (
-            'sitename', 'logo_string', 'navi_bar', 'page_front_page',
-            'page_category_regex', 'page_dict_regex',
-            'page_group_regex', 'page_template_regex', 'page_license_page',
-            'page_local_spelling_words', 'acl_rights_default',
-            'acl_rights_before', 'acl_rights_after', 'mail_from'
-            )
-
-        for name in decode_names:
-            attr = getattr(self, name, None)
-            if attr:
-                # Try to decode strings
-                if isinstance(attr, str):
-                    try:
-                        setattr(self, name, unicode(attr, charset))
-                    except UnicodeError:
-                        raise error.ConfigurationError(message %
-                                                       {'name': name})
-                # Look into lists and try to decode strings inside them
-                elif isinstance(attr, list):
-                    for i in xrange(len(attr)):
-                        item = attr[i]
-                        if isinstance(item, str):
-                            try:
-                                attr[i] = unicode(item, charset)
-                            except UnicodeError:
-                                raise error.ConfigurationError(message %
-                                                               {'name': name})
-
-    def _check_directories(self):
-        """ Make sure directories are accessible
-
-        Both data and underlay should exists and allow read, write and
-        execute.
-        """
-        mode = os.F_OK | os.R_OK | os.W_OK | os.X_OK
-        for attr in ('data_dir', 'data_underlay_dir'):
-            path = getattr(self, attr)
-
-            # allow an empty underlay path or None
-            if attr == 'data_underlay_dir' and not path:
-                continue
-
-            path_pages = os.path.join(path, "pages")
-            if not (os.path.isdir(path_pages) and os.access(path_pages, mode)):
-                msg = '''
-%(attr)s "%(path)s" does not exists, or has incorrect ownership or
-permissions.
-
-Make sure the directory and the subdirectory pages are owned by the web
-server and are readable, writable and executable by the web server user
-and group.
-
-It is recommended to use absolute paths and not relative paths. Check
-also the spelling of the directory name.
-''' % {'attr': attr, 'path': path, }
-                raise error.ConfigurationError(msg)
-
-    def _loadPluginModule(self):
-        """ import plugin module under configname.plugin
-
-        To be able to import plugin from arbitrary path, we have to load
-        the base package once using imp.load_module. Later, we can use
-        standard __import__ call to load plugins in this package.
-
-        Since each wiki has unique plugins, we load the plugin package
-        under the wiki configuration module, named self.siteid.
-        """
-        import sys, imp
-
-        name = self.siteid + '.plugin'
-        try:
-            # Lock other threads while we check and import
-            imp.acquire_lock()
-            try:
-                # If the module is not loaded, try to load it
-                if not name in sys.modules:
-                    # Find module on disk and try to load - slow!
-                    plugin_parent_dir = os.path.abspath(os.path.join(self.plugin_dir, '..'))
-                    fp, path, info = imp.find_module('plugin', [plugin_parent_dir])
-                    try:
-                        # Load the module and set in sys.modules             
-                        module = imp.load_module(name, fp, path, info)
-                        sys.modules[self.siteid].plugin = module
-                    finally:
-                        # Make sure fp is closed properly
-                        if fp:
-                            fp.close()
-            finally:
-                imp.release_lock()
-        except ImportError, err:
-            msg = '''
-Could not import plugin package "%(path)s/plugin" because of ImportError:
-%(err)s.
-
-Make sure your data directory path is correct, check permissions, and
-that the data/plugin directory has an __init__.py file.
-''' % {
-    'path': self.data_dir,
-    'err': str(err),
-}
-            raise error.ConfigurationError(msg)
-
-    def _fillDicts(self):
-        """ fill config dicts
-
-        Fills in missing dict keys of derived user config by copying
-        them from this base class.
-        """
-        # user checkbox defaults
-        for key, value in DefaultConfig.user_checkbox_defaults.items():
-            if not self.user_checkbox_defaults.has_key(key):
-                self.user_checkbox_defaults[key] = value
-
-    def __getitem__(self, item):
-        """ Make it possible to access a config object like a dict """
-        return getattr(self, item)
-
-# remove the gettext pseudo function 
-del _
-
--- a/MoinMoin/request/CGI.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/CGI.py	Mon Jul 24 22:18:49 2006 +0200
@@ -7,7 +7,7 @@
                 2003-2006 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
-import sys, os
+import sys, os, cgi
 
 from MoinMoin import config
 from MoinMoin.request import RequestBase
@@ -29,6 +29,11 @@
         except Exception, err:
             self.fail(err)
 
+    def _setup_args_from_cgi_form(self):
+        """ Override to create cgi form """
+        form = cgi.FieldStorage()
+        return RequestBase._setup_args_from_cgi_form(self, form)
+
     def open_logs(self):
         # create log file for catching stderr output
         if not self.opened_logs:
--- a/MoinMoin/request/CLI.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/CLI.py	Mon Jul 24 22:18:49 2006 +0200
@@ -30,6 +30,12 @@
         self.cfg.caching_formats = [] # don't spoil the cache
         self.initTheme() # usually request.run() does this, but we don't use it
 
+    def _setup_args_from_cgi_form(self):
+        """ Override to create cli form """
+        #form = cgi.FieldStorage()
+        #return RequestBase._setup_args_from_cgi_form(self, form)
+        return {}
+
     def read(self, n=None):
         """ Read from input stream. """
         if n is None:
--- a/MoinMoin/request/FCGI.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/FCGI.py	Mon Jul 24 22:18:49 2006 +0200
@@ -32,11 +32,9 @@
         except Exception, err:
             self.fail(err)
 
-    def _setup_args_from_cgi_form(self, form=None):
+    def _setup_args_from_cgi_form(self):
         """ Override to use FastCGI form """
-        if form is None:
-            form = self.fcgform
-        return RequestBase._setup_args_from_cgi_form(self, form)
+        return RequestBase._setup_args_from_cgi_form(self, self.fcgform)
 
     def read(self, n=None):
         """ Read from input stream. """
--- a/MoinMoin/request/MODPYTHON.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/MODPYTHON.py	Mon Jul 24 22:18:49 2006 +0200
@@ -76,15 +76,14 @@
 
         RequestBase.fixURI(self, env)
 
-    def _setup_args_from_cgi_form(self, form=None):
+    def _setup_args_from_cgi_form(self):
         """ Override to use mod_python.util.FieldStorage 
         
-        Its little different from cgi.FieldStorage, so we need to
+        It's little different from cgi.FieldStorage, so we need to
         duplicate the conversion code.
         """
         from mod_python import util
-        if form is None:
-            form = util.FieldStorage(self.mpyreq)
+        form = util.FieldStorage(self.mpyreq)
 
         args = {}
         for key in form.keys():
--- a/MoinMoin/request/STANDALONE.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/STANDALONE.py	Mon Jul 24 22:18:49 2006 +0200
@@ -54,7 +54,7 @@
         except Exception, err:
             self.fail(err)
 
-    def _setup_args_from_cgi_form(self, form=None):
+    def _setup_args_from_cgi_form(self):
         """ Override to create standalone form """
         form = cgi.FieldStorage(self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST'})
         return RequestBase._setup_args_from_cgi_form(self, form)
--- a/MoinMoin/request/TWISTED.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/TWISTED.py	Mon Jul 24 22:18:49 2006 +0200
@@ -64,7 +64,7 @@
             return self.finish()
         RequestBase.run(self)
 
-    def setup_args(self, form=None):
+    def setup_args(self):
         """ Return args dict 
         
         Twisted already parsed args, including __filename__ hacking,
--- a/MoinMoin/request/WSGI.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/WSGI.py	Mon Jul 24 22:18:49 2006 +0200
@@ -6,7 +6,7 @@
                 2003-2006 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
-import sys, os
+import sys, os, cgi
 
 from MoinMoin import config
 from MoinMoin.request import RequestBase
@@ -30,12 +30,11 @@
         except Exception, err:
             self.fail(err)
 
-    def setup_args(self, form=None):
+    def setup_args(self):
         # TODO: does this include query_string args for POST requests?
         # see also how CGI works now
-        if form is None:
-            form = cgi.FieldStorage(fp=self.stdin, environ=self.env, keep_blank_values=1)
-        return self._setup_args_from_cgi_form(form)
+        form = cgi.FieldStorage(fp=self.stdin, environ=self.env, keep_blank_values=1)
+        return RequestBase._setup_args_from_cgi_form(self, form)
 
     def read(self, n=None):
         if n is None:
--- a/MoinMoin/request/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/request/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -141,11 +141,13 @@
             #    # no extra path after script name
             #    rootname = u""
 
-            self.args = {}
-            self.form = {}
-
-            if not self.query_string.startswith('action=xmlrpc'):
+            if self.query_string.startswith('action=xmlrpc'):
+                self.args = {}
+                self.form = {}
+                self.action = 'xmlrpc'
+            else:
                 self.args = self.form = self.setup_args()
+                self.action = self.form.get('action', ['show'])[0]
 
             rootname = u''
             self.rootpage = Page(self, rootname, is_rootpage=1)
@@ -156,7 +158,7 @@
 
             self.user = self.get_user_from_form()
 
-            if not self.query_string.startswith('action=xmlrpc'):
+            if self.action != 'xmlrpc':
                 if not self.forbidden and self.isForbidden():
                     self.makeForbidden403()
                 if not self.forbidden and self.surge_protect():
@@ -181,7 +183,7 @@
         current_id = validuser and self.user.name or self.remote_addr
         if not validuser and current_id.startswith('127.'): # localnet
             return False
-        current_action = self.form.get('action', ['show'])[0]
+        current_action = self.action
 
         limits = self.cfg.surge_action_limits
         default_limit = self.cfg.surge_action_limits.get('default', (30, 60))
@@ -263,7 +265,7 @@
     def _load_multi_cfg(self):
         # protect against calling multiple times
         if not hasattr(self, 'cfg'):
-            from MoinMoin import multiconfig
+            from MoinMoin.config import multiconfig
             self.cfg = multiconfig.getConfig(self.url)
 
     def setAcceptedCharsets(self, accept_charset):
@@ -544,7 +546,6 @@
         # during the rendering of a page by lang macros
         self.current_lang = self.cfg.language_default
 
-        self._all_pages = None
         # caches unique ids
         self._page_ids = {}
         # keeps track of pagename/heading combinations
@@ -863,11 +864,12 @@
         forbidden = 0
         # we do not have a parsed query string here, so we can just do simple matching
         qs = self.query_string
+        action = self.action
         if ((qs != '' or self.request_method != 'GET') and
-            not 'action=rss_rc' in qs and
+            action != 'rss_rc' and
             # allow spiders to get attachments and do 'show'
-            not ('action=AttachFile' in qs and 'do=get' in qs) and
-            not 'action=show' in qs
+            not (action == 'AttachFile' and 'do=get' in qs) and
+            action != 'show'
             ):
             forbidden = self.isSpiderAgent
 
@@ -884,40 +886,33 @@
                     break
         return forbidden
 
-    def setup_args(self, form=None):
+    def setup_args(self):
         """ Return args dict 
         First, we parse the query string (usually this is used in GET methods,
         but TwikiDraw uses ?action=AttachFile&do=savedrawing plus posted stuff).
         Second, we update what we got in first step by the stuff we get from
         the form (or by a POST). We invoke _setup_args_from_cgi_form to handle
         possible file uploads.
-        
-        Warning: calling with a form might fail, depending on the type of the
-        request! Only the request knows which kind of form it can handle.
-        
-        TODO: The form argument should be removed in 1.5.
         """
         args = cgi.parse_qs(self.query_string, keep_blank_values=1)
         args = self.decodeArgs(args)
-        # if we have form data (e.g. in a POST), those override the stuff we already have:
-        if form is not None or self.request_method == 'POST':
-            postargs = self._setup_args_from_cgi_form(form)
+        # if we have form data (in a POST), those override the stuff we already have:
+        if self.request_method == 'POST':
+            postargs = self._setup_args_from_cgi_form()
             args.update(postargs)
         return args
 
     def _setup_args_from_cgi_form(self, form=None):
         """ Return args dict from a FieldStorage
-        
-        Create the args from a standard cgi.FieldStorage or from given form.
-        Each key contain a list of values.
+
+        Create the args from a given form. Each key contain a list of values.
+        This method usually gets overridden in classes derived from this - it
+        is their task to call this method with an appropriate form parameter.
 
         @param form: a cgi.FieldStorage
         @rtype: dict
         @return: dict with form keys, each contains a list of values
         """
-        if form is None:
-            form = cgi.FieldStorage()
-
         args = {}
         for key in form:
             values = form[key]
@@ -1028,22 +1023,19 @@
         self.html_formatter = Formatter(self)
         self.formatter = self.html_formatter
 
-        if self.query_string == 'action=xmlrpc':
+        action_name = self.action
+        if action_name == 'xmlrpc':
             from MoinMoin import xmlrpc
-            xmlrpc.xmlrpc(self)
-            return self.finish()
-
-        if self.query_string == 'action=xmlrpc2':
-            from MoinMoin import xmlrpc
-            xmlrpc.xmlrpc2(self)
+            if self.query_string == 'action=xmlrpc':
+                xmlrpc.xmlrpc(self)
+            elif self.query_string == 'action=xmlrpc2':
+                xmlrpc.xmlrpc2(self)
             return self.finish()
 
         # parse request data
         try:
             self.initTheme()
 
-            action_name = self.form.get('action', ['show'])[0]
-
             # The last component in path_info is the page name, if any
             path = self.getPathinfo()
             if path.startswith('/'):
@@ -1098,18 +1090,21 @@
                 else:
                     self.page = Page(self, pagename)
 
+                msg = None
                 # Complain about unknown actions
                 if not action_name in self.getKnownActions():
-                    self.http_headers()
-                    self.write(u'<html><body><h1>Unknown action %s</h1></body>' % wikiutil.escape(action_name))
+                    msg = _("Unknown action %(action_name)s.") % {
+                            'action_name': wikiutil.escape(action_name), }
 
                 # Disallow non available actions
                 elif action_name[0].isupper() and not action_name in self.getAvailableActions(self.page):
-                    # Send page with error
-                    msg = _("You are not allowed to do %s on this page.") % wikiutil.escape(action_name)
+                    msg = _("You are not allowed to do %(action_name)s on this page.") % {
+                            'action_name': wikiutil.escape(action_name), }
                     if not self.user.valid:
                         # Suggest non valid user to login
                         msg += " " + _("Login and try again.", formatted=0)
+
+                if msg:
                     self.page.send_page(self, msg=msg)
 
                 # Try action
--- a/MoinMoin/script/cli/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/script/cli/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -1,6 +1,6 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - Migration Script Package
+    MoinMoin - CLI usage Script Package
 
     @copyright: 2006 by Thomas Waldmann
     @license: GNU GPL, see COPYING for details.
@@ -9,6 +9,6 @@
 from MoinMoin.util import pysupport
 
 # create a list of extension scripts from the subpackage directory
-migration_scripts = pysupport.getPackageModules(__file__)
-modules = migration_scripts
+cli_scripts = pysupport.getPackageModules(__file__)
+modules = cli_scripts
 
--- a/MoinMoin/stats/hitcounts.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/stats/hitcounts.py	Mon Jul 24 22:18:49 2006 +0200
@@ -47,16 +47,17 @@
 
 
 def get_data(pagename, request, filterpage=None):
+    cache_days, cache_views, cache_edits = [], [], []
+    cache_date = 0
 
     # Get results from cache
     if filterpage:
         arena = Page(request, pagename)
+        cache = caching.CacheEntry(request, arena, 'hitcounts', scope='item')
     else:
         arena = 'charts'
+        cache = caching.CacheEntry(request, arena, 'hitcounts', scope='wiki')
 
-    cache_days, cache_views, cache_edits = [], [], []
-    cache_date = 0
-    cache = caching.CacheEntry(request, arena, 'hitcounts', scope='wiki')
     if cache.exists():
         try:
             cache_date, cache_days, cache_views, cache_edits = eval(cache.content())
--- a/MoinMoin/theme/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/theme/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -20,9 +20,9 @@
     use without rewriting the same code. If you want to change certain 
     elements, override them. 
     """
-    
+
     name = 'base'
-    
+
     # fake _ function to get gettext recognize those texts:
     _ = lambda x: x
 
@@ -64,7 +64,7 @@
         # search forms
         'searchbutton': ("[?]",                  "moin-search.png", 12, 12),
         'interwiki':  ("[%(wikitag)s]",          "moin-inter.png",  16, 16),
-    
+
         # smileys (this is CONTENT, but good looking smileys depend on looking
         # adapted to the theme background color and theme style in general)
         #vvv    ==      vvv  this must be the same for GUI editor converter
@@ -86,7 +86,7 @@
         ':\\':        (":\\",                    'ohwell.png',      15, 15),
         '>:>':        (">:>",                    'devil.png',       15, 15),
         '|)':         ("|)",                     'tired.png',       15, 15),
-        
+
         # some folks use noses in their emoticons
         ':-(':        (":-(",                    'sad.png',         15, 15),
         ':-)':        (":-)",                    'smile.png',       15, 15),
@@ -94,7 +94,7 @@
         ':-))':       (":-))",                   'smile3.png',      15, 15),
         ';-)':        (";-)",                    'smile4.png',      15, 15),
         '|-)':        ("|-)",                    'tired.png',       15, 15),
-        
+
         # version 1.0
         '(./)':       ("(./)",                   'checkmark.png',   20, 15),
         '{OK}':       ("{OK}",                   'thumbs-up.png',   14, 12),
@@ -135,7 +135,7 @@
         # media         basename
         ('all',         'common'),
         ('all',         'projection'),
-       )   
+       )
 
     stylesheetsCharset = 'utf-8'
 
@@ -157,7 +157,7 @@
         @return: the image href
         """
         return "%s/%s/img/%s" % (self.cfg.url_prefix, self.name, img)
-        
+
     def emit_custom_html(self, html):
         """
         generate custom HTML code in `html`
@@ -196,15 +196,15 @@
         @rtype: string
         @return: interwiki html
         """
-        html = u''
         if self.request.cfg.show_interwiki:
-            # Show our interwikiname or Self (and link to page_front_page)
-            pagename = wikiutil.getFrontPage(self.request).page_name
-            pagename = wikiutil.quoteWikinameURL(pagename)
-            link = wikiutil.link_tag(self.request, pagename, self.request.cfg.interwikiname or 'Self')
+            page = wikiutil.getFrontPage(self.request)
+            text = self.request.cfg.interwikiname or 'Self'
+            link = page.link_to(self.request, text=text, rel='nofollow')
             html = u'<div id="interwiki"><span>%s</span></div>' % link
+        else:
+            html = u''
         return html
-        
+
     def title(self, d):
         """ Assemble the title (now using breadcrumbs)
         
@@ -246,7 +246,7 @@
         """
         request = self.request
         _ = request.getText
-        
+
         userlinks = []
         # Add username/homepage link for registered users. We don't care
         # if it exists, the user can create it.
@@ -261,11 +261,11 @@
             homelink = (request.formatter.interwikilink(1, title=title, id="userhome", *interwiki) +
                         request.formatter.text(name) +
                         request.formatter.interwikilink(0, title=title, id="userhome", *interwiki))
-            userlinks.append(homelink)        
+            userlinks.append(homelink)
             # link to userprefs action
             userlinks.append(d['page'].link_to(request, text=_('Preferences'),
                                                querystr={'action': 'userprefs'}, id='userprefs', rel='nofollow'))
-           
+
         if request.cfg.show_login:
             if request.user.valid:
                 userlinks.append(d['page'].link_to(request, text=_('Logout', formatted=False),
@@ -295,7 +295,7 @@
         @return: pagename or url, link to page or url
         """
         request = self.request
-        
+
         # Handle [pagename title] or [url title] formats
         if text.startswith('[') and text.endswith(']'):
             try:
@@ -343,10 +343,10 @@
                         page +
                         self.request.formatter.interwikilink(False, interwiki, page)
                         )
-        
+
         except ValueError:
             pass
-                  
+
         pagename = request.normalizePagename(pagename)
         link = Page(request, pagename).link_to(request, title)
 
@@ -375,10 +375,10 @@
                 half, left = divmod(maxLength - 3, 2)
                 name = u'%s...%s' % (name[:half + left], name[-half:])
         return name
-        
+
     def maxPagenameLength(self):
         """ Return maximum length for shortened page names """
-        return 25 
+        return 25
 
     def navibar(self, d):
         """ Assemble the navibar
@@ -461,9 +461,9 @@
                 alt, icon, w, h = self.iconsByFile[icon]
             else:
                 alt, icon, w, h = '', icon, '', ''
-                
+
         return alt, self.img_url(icon), w, h
-   
+
     def make_icon(self, icon, vars=None):
         """
         This is the central routine for making <img> tags for icons!
@@ -527,13 +527,13 @@
             close = d['page'].link_to(self.request,
                                       text=_('Clear message'),
                                       querystr={'action': 'show'})
-            html = u'<p>%s</p>\n<div class="buttons">%s</div>\n' % (msg, close) 
+            html = u'<p>%s</p>\n<div class="buttons">%s</div>\n' % (msg, close)
         else:
             # msg is a widget
             html = msg.render()
 
         return u'<div id="message">\n%s\n</div>\n' % html
-        
+
     def trail(self, d):
         """ Assemble page trail
         
@@ -559,7 +559,7 @@
                             continue
                         else:
                             pagename = page
-                            
+
                     except ValueError:
                         pass
                     page = Page(request, pagename)
@@ -606,9 +606,10 @@
         for media, csshref in self.request.cfg.stylesheets:
             html.append(link % (self.stylesheetsCharset, media, csshref))
 
-        
+
         # tribute to the most sucking browser...
-        if self.cfg.hacks.get('ie7', False):
+        if self.cfg.hacks.get('ie7', False) and self.request.action != 'edit':
+            # using FCKeditor and IE7 at the same time makes nices crashes in IE
             html.append("""
 <!-- compliance patch for microsoft browsers -->
 <!--[if lt IE 7]>
@@ -619,7 +620,7 @@
         # Add user css url (assuming that user css uses same charset)
         if usercss and usercss.lower() != "none":
             html.append(link % (self.stylesheetsCharset, 'all', usercss))
-            
+
         return '\n'.join(html)
 
     def shouldShowPageinfo(self, page):
@@ -640,10 +641,9 @@
             contentActions = [u'', u'show', u'refresh', u'preview', u'diff',
                               u'subscribe', u'RenamePage', u'DeletePage',
                               u'SpellCheck', u'print']
-            action = self.request.form.get('action', [''])[0]
-            return action in contentActions
+            return self.request.action in contentActions
         return False
-    
+
     def pageinfo(self, page):
         """ Return html fragment with page meta data
 
@@ -673,7 +673,7 @@
                     'info': info
                     }
         return html
-    
+
     def searchform(self, d):
         """
         assemble HTML code for the search forms
@@ -685,10 +685,10 @@
         _ = self.request.getText
         form = self.request.form
         updates = {
-            'search_label' : _('Search:'),
+            'search_label': _('Search:'),
             'search_value': wikiutil.escape(form.get('value', [''])[0], 1),
-            'search_full_label' : _('Text', formatted=False),
-            'search_title_label' : _('Titles', formatted=False),
+            'search_full_label': _('Text', formatted=False),
+            'search_title_label': _('Titles', formatted=False),
             }
         d.update(updates)
 
@@ -732,7 +732,7 @@
             html = (u'<div id="version">MoinMoin Release %s [Revision %s], '
                      'Copyright 2000-2006 by Juergen Hermann</div>') % (version.release, version.revision, )
         return html
-    
+
     def headscript(self, d):
         """ Return html head script with common functions
 
@@ -747,9 +747,9 @@
         @return: script for html head
         """
         # Don't add script for print view
-        if self.request.form.get('action', [''])[0] == 'print':
+        if self.request.action == 'print':
             return u''
-        
+
         _ = self.request.getText
         script = u"""
 <script type=\"text/javascript\">
@@ -820,10 +820,10 @@
 //-->
 </script>
 """ % {
-    'search_hint' : _('Search', formatted=False),
+    'search_hint': _('Search', formatted=False),
     }
         return script
-    
+
     def shouldUseRSS(self):
         """ Return True if rss feature is available, or False
 
@@ -837,16 +837,16 @@
         except ImportError:
             # This error reported on Python 2.2
             return False
-        
+
     def rsshref(self):
         """ Create rss href, used for rss button and head link
         
         @rtype: unicode
         @return: rss href
         """
-        return (u'%s/RecentChanges?action=rss_rc&amp;ddiffs=1&amp;unique=1' 
+        return (u'%s/RecentChanges?action=rss_rc&amp;ddiffs=1&amp;unique=1'
                 % self.request.getScriptname())
-                           
+
     def rsslink(self):
         """ Create rss link in head, used by FireFox
 
@@ -863,7 +863,7 @@
                         self.cfg.sitename,
                         self.rsshref() )
         return link
-       
+
     def html_head(self, d):
         """ Assemble html head
         
@@ -917,7 +917,7 @@
         """
         request = self.request
         _ = request.getText
-        
+
         menu = [
             'raw',
             'print',
@@ -964,7 +964,7 @@
         # "disabled", e.g IE, Safari
         # for XHTML: data['disabled'] = ' disabled="disabled"'
         disabled = ' disabled class="disabled"'
-        
+
         # Format standard actions
         available = request.getAvailableActions(page)
         for action in menu:
@@ -984,7 +984,7 @@
             # Actions which are not available for this wiki, user or page
             if (action == '__separator__' or
                 (action[0].isupper() and not action in available)):
-                data['disabled'] = disabled               
+                data['disabled'] = disabled
 
             options.append(option % data)
 
@@ -1034,9 +1034,9 @@
 </script>
 </form>
 ''' % data
-        
-        return html      
-        
+
+        return html
+
     def editbar(self, d):
         """ Assemble the page edit bar.
 
@@ -1057,7 +1057,7 @@
                              for item in self.editbarItems(page) if item])
             html = u'<ul class="editbar">%s</ul>\n' % items
             self._cache['editbar'] = html
-        
+
         return html
 
     def shouldShowEditbar(self, page):
@@ -1077,7 +1077,7 @@
         if (page.exists(includeDeleted=1) and
             self.request.user.may.read(page.page_name)):
             form = self.request.form
-            action = form.get('action', [''])[0]
+            action = self.request.action
             # Do not show editbar on edit but on save/cancel
             return not (action == 'edit' and
                         not form.has_key('button_save') and
@@ -1095,7 +1095,8 @@
                 self.subscribeLink(page),
                 self.quicklinkLink(page),
                 self.attachmentsLink(page),
-                self.actionsMenu(page),]
+                self.actionsMenu(page),
+               ]
 
     def guiworks(self, page):
         """ Return whether the gui editor / converter can work for that page.
@@ -1103,7 +1104,7 @@
             The GUI editor currently only works for wiki format.
         """
         return page.pi_format == 'wiki'
-        
+
     def editorLink(self, page):
         """ Return a link to the editor 
         
@@ -1115,27 +1116,26 @@
         if not (page.isWritable() and
                 self.request.user.may.write(page.page_name)):
             return self.disabledEdit()
-        
+
         _ = self.request.getText
-        params = (wikiutil.quoteWikinameURL(page.page_name) +
-                  '?action=edit&amp;editor=')
-        
+        querystr = {'action': 'edit'}
+
         guiworks = self.guiworks(page)
         if self.showBothEditLinks() and guiworks:
             text = _('Edit (Text)', formatted=False)
-            params = params + 'text'
+            querystr['editor'] = 'text'
             attrs = {'name': 'texteditlink', 'rel': 'nofollow', }
         else:
             text = _('Edit', formatted=False)
             if guiworks:
                 # 'textonly' will be upgraded dynamically to 'guipossible' by JS
-                params = params + 'textonly'
+                querystr['editor'] = 'textonly'
                 attrs = {'name': 'editlink', 'rel': 'nofollow', }
             else:
-                params = params + 'text'
+                querystr['editor'] = 'text'
                 attrs = {'name': 'texteditlink', 'rel': 'nofollow', }
-        
-        return wikiutil.link_tag(self.request, params, text, **attrs)
+
+        return page.link_to(self.request, text=text, querystr=querystr, **attrs)
 
     def showBothEditLinks(self):
         """ Return True if both edit links should be displayed """
@@ -1164,21 +1164,22 @@
 var gui_editor_link_text = "%(text)s";
 </script>        
 """ % {'url': page.url(self.request),
-       'text': _('Edit (GUI)', formatted=False),}
+       'text': _('Edit (GUI)', formatted=False),
+      }
 
     def disabledEdit(self):
         """ Return a disabled edit link """
         _ = self.request.getText
-        return ('<span class="disabled">%s</span>' 
+        return ('<span class="disabled">%s</span>'
                 % _('Immutable Page', formatted=False))
-        
+
     def infoLink(self, page):
         """ Return link to page information """
         _ = self.request.getText
         return page.link_to(self.request,
-                            text=_('Info', formatted=False), 
-                            querystr='action=info', rel='nofollow')
-    
+                            text=_('Info', formatted=False),
+                            querystr={'action': 'info'}, id='info', rel='nofollow')
+
     def subscribeLink(self, page):
         """ Return subscribe/unsubscribe link to valid users
         
@@ -1187,14 +1188,13 @@
         """
         if not (self.cfg.mail_enabled and self.request.user.valid):
             return ''
-        
+
         _ = self.request.getText
         if self.request.user.isSubscribedTo([page.page_name]):
             text = _("Unsubscribe", formatted=False)
         else:
             text = _("Subscribe", formatted=False)
-        params = wikiutil.quoteWikinameURL(page.page_name) + '?action=subscribe'
-        return wikiutil.link_tag(self.request, params, text, self.request.formatter, rel='nofollow')
+        return page.link_to(self.request, text=text, querystr={'action': 'subscribe'}, id='subscribe', rel='nofollow')
 
     def quicklinkLink(self, page):
         """ Return add/remove quicklink link
@@ -1204,21 +1204,20 @@
         """
         if not self.request.user.valid:
             return ''
-        
+
         _ = self.request.getText
         if self.request.user.isQuickLinkedTo([page.page_name]):
             text = _("Remove Link", formatted=False)
         else:
             text = _("Add Link", formatted=False)
-        params = wikiutil.quoteWikinameURL(page.page_name) + '?action=quicklink'
-        return wikiutil.link_tag(self.request, params, text, self.request.formatter, rel='nofollow')
+        return page.link_to(self.request, text=text, querystr={'action': 'quicklink'}, id='quicklink', rel='nofollow')
 
     def attachmentsLink(self, page):
         """ Return link to page attachments """
         _ = self.request.getText
         return page.link_to(self.request,
-                            text=_('Attachments', formatted=False), 
-                            querystr='action=AttachFile', rel='nofollow')
+                            text=_('Attachments', formatted=False),
+                            querystr={'action': 'AttachFile'}, id='attachments', rel='nofollow')
 
     def startPage(self):
         """ Start page div with page language and direction
@@ -1227,15 +1226,15 @@
         @return: page div with language and direction attribtues
         """
         return u'<div id="page"%s>\n' % self.content_lang_attr()
-            
+
     def endPage(self):
         """ End page div 
         
         Add an empty page bottom div to prevent floating elements to
         float out of the page bottom over the footer.
         """
-        return '<div id="pagebottom"></div>\n</div>\n'        
-    
+        return '<div id="pagebottom"></div>\n</div>\n'
+
     # Public functions #####################################################
 
     def header(self, d, **kw):
@@ -1249,9 +1248,9 @@
         @return: page header html
         """
         return self.startPage()
-    
+
     editorheader = header
-        
+
     def footer(self, d, **keywords):
         """ Assemble page footer
         
@@ -1278,40 +1277,40 @@
         _ = self.request.getText
         html = []
         html.append('<tr>\n')
-        
+
         html.append('<td class="rcicon1">%(icon_html)s</td>\n' % d)
-        
+
         html.append('<td class="rcpagelink">%(pagelink_html)s</td>\n' % d)
-        
+
         html.append('<td class="rctime">')
         if d['time_html']:
             html.append("%(time_html)s" % d)
         html.append('</td>\n')
 
         html.append('<td class="rcicon2">%(info_html)s</td>\n' % d)
-        
+
         html.append('<td class="rceditor">')
         if d['editors']:
             html.append('<br>'.join(d['editors']))
         html.append('</td>\n')
-            
+
         html.append('<td class="rccomment">')
         if d['comments']:
             if d['changecount'] > 1:
                 notfirst = 0
                 for comment in d['comments']:
                     html.append('%s<tt>#%02d</tt>&nbsp;%s' % (
-                        notfirst and '<br>' or '' , comment[0], comment[1]))
+                        notfirst and '<br>' or '', comment[0], comment[1]))
                     notfirst = 1
             else:
                 comment = d['comments'][0]
                 html.append('%s' % comment[1])
         html.append('</td>\n')
-           
+
         html.append('</tr>\n')
-        
+
         return ''.join(html)
-    
+
     def recentchanges_daybreak(self, d):
         """
         Assemble a rc daybreak indication (table row)
@@ -1338,7 +1337,7 @@
         @return: recentchanges header html
         """
         _ = self.request.getText
-        
+
         # Should use user interface language and direction
         html = '<div class="recentchanges"%s>\n' % self.ui_lang_attr()
         html += '<div>\n'
@@ -1366,7 +1365,7 @@
                             self.request.formatter, rel='nofollow'))
             days = ' | '.join(days)
             html += (_("Show %s days.") % (days,))
-        
+
         if d['rc_update_bookmark']:
             html += " %(rc_update_bookmark)s %(rc_curr_bookmark)s" % d
 
@@ -1392,7 +1391,7 @@
         return html
 
     # Language stuff ####################################################
-    
+
     def ui_lang_attr(self):
         """Generate language attributes for user interface elements
 
@@ -1439,14 +1438,14 @@
         """
         request = self.request
         _ = request.getText
-        
+
         if keywords.has_key('page'):
             page = keywords['page']
             pagename = page.page_name
         else:
             pagename = keywords.get('pagename', '')
             page = Page(request, pagename)
-        
+
         scriptname = request.getScriptname()
         pagename_quoted = wikiutil.quoteWikinameURL(pagename)
 
@@ -1461,7 +1460,7 @@
         page_find_page = wikiutil.getSysPage(request, 'FindPage').page_name
         home_page = wikiutil.getInterwikiHomePage(request) # XXX sorry theme API change!!! Either None or tuple (wikiname,pagename) now.
         page_parent_page = getattr(page.getParentPage(), 'page_name', None)
-        
+
         # Prepare the HTML <head> element
         user_head = [request.cfg.html_head]
 
@@ -1492,7 +1491,7 @@
                           page_title_index, 'TitleIndex',
                           page_find_page, 'FindPage',
                           page_site_navigation, 'SiteNavigation',
-                          'RecentChanges',]:
+                          'RecentChanges', ]:
             user_head.append(request.cfg.html_head_index)
         # if it is a normal page, index it, but do not follow the links, because
         # there are a lot of illegal links (like actions) or duplicates:
@@ -1501,7 +1500,7 @@
 
         if keywords.has_key('pi_refresh') and keywords['pi_refresh']:
             user_head.append('<meta http-equiv="refresh" content="%(delay)d;URL=%(url)s">' % keywords['pi_refresh'])
-        
+
         # output buffering increases latency but increases throughput as well
         output = []
         # later: <html xmlns=\"http://www.w3.org/1999/xhtml\">
@@ -1566,7 +1565,7 @@
             '<link rel="Glossary" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_word_index)),
             '<link rel="Help" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_help_formatting)),
                       ])
-        
+
         output.append("</head>\n")
         request.write(''.join(output))
         output = []
@@ -1590,7 +1589,7 @@
 
         # Set body to the user interface language and direction
         bodyattr.append(' %s' % self.ui_lang_attr())
-        
+
         body_onload = keywords.get('body_onload', '')
         if body_onload:
             bodyattr.append(''' onload="%s"''' % body_onload)
@@ -1600,11 +1599,11 @@
 
         # If in print mode, start page div and emit the title
         if keywords.get('print_mode', 0):
-            d = {'title_text': text, 'title_link': None, 'page': page,}
+            d = {'title_text': text, 'title_link': None, 'page': page, }
             request.themedict = d
             output.append(self.startPage())
-            output.append(self.interwiki(d))      
-            output.append(self.title(d))      
+            output.append(self.interwiki(d))
+            output.append(self.title(d))
 
         # In standard mode, emit theme.header
         else:
@@ -1657,7 +1656,7 @@
                 output.append(self.editorheader(d))
             else:
                 output.append(self.header(d))
-        
+
         # emit it
         request.write(''.join(output))
         output = []
@@ -1693,8 +1692,7 @@
         request.clock.stop('total')
 
         # Close html code
-        if (request.cfg.show_timings and
-            request.form.get('action', [None])[0] != 'print'):
+        if request.cfg.show_timings and request.action != 'print':
             request.write('<ul id="timings">\n')
             for t in request.clock.dump():
                 request.write('<li>%s</li>\n' % t)
--- a/MoinMoin/theme/classic.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/theme/classic.py	Mon Jul 24 22:18:49 2006 +0200
@@ -8,7 +8,7 @@
     If you want modified behaviour, just override the stuff you
     want to change in the child class.
 
-    @copyright: 2003 by ThomasWaldmann (LinuxWiki:ThomasWaldmann)
+    @copyright: 2003-2006 by MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -22,7 +22,7 @@
     """ here are the functions generating the html responsible for
         the look and feel of your wiki site
     """
-    
+
     name = "classic"
 
     def footer(self, d, **keywords):
@@ -45,9 +45,9 @@
                  self.editbar(d, **keywords),
                  self.credits(d),
                  self.showversion(d, **keywords),
-                 
+
                  # Post footer custom html
-                 self.emit_custom_html(self.cfg.page_footer2),]
+                 self.emit_custom_html(self.cfg.page_footer2), ]
         return u'\n'.join(parts)
 
     def editbar(self, d, **keywords):
@@ -56,7 +56,7 @@
         parts = [u'<div id="footer">',
                  self.edit_link(d, **keywords),
                  self.availableactions(d),
-                 u'</div>',]
+                 u'</div>', ]
         return ''.join(parts)
 
     def iconbar(self, d):
@@ -82,7 +82,7 @@
                     iconbar.append('<li>%s</li>\n' % self.make_iconlink(icon, d))
             iconbar.append('</ul>\n')
         return ''.join(iconbar)
-    
+
     def header(self, d):
         """
         Assemble page header
@@ -95,10 +95,10 @@
             'config_header1_html': self.emit_custom_html(self.cfg.page_header1),
             'config_header2_html': self.emit_custom_html(self.cfg.page_header2),
             'search_form_html': self.searchform(d),
-            'logo_html':  self.logo(),
-            'interwiki_html':  self.interwiki(d),
-            'title_html':  self.title(d),
-            'username_html':  self.username(d),
+            'logo_html': self.logo(),
+            'interwiki_html': self.interwiki(d),
+            'title_html': self.title(d),
+            'username_html': self.username(d),
             'navibar_html': self.navibar(d),
             'iconbar_html': self.iconbar(d),
             'msg_html': self.msg(d),
@@ -160,7 +160,7 @@
         return html
 
     # Footer stuff #######################################################
-    
+
     def edit_link(self, d, **keywords):
         """
         Assemble EditText link (or indication that page cannot be edited)
@@ -172,7 +172,7 @@
         page = d['page']
         return  u'<ul class="editbar"><li>%s</li></ul>' % self.editorLink(page)
 
-    def availableactions(self, d):    
+    def availableactions(self, d):
         """
         assemble HTML code for the available actions
         
@@ -194,14 +194,12 @@
                 title = Page(request, action).split_title(request, force=1)
                 # Use translated version if available
                 title = _(title, formatted=False)
-                params = '%s?action=%s' % (d['q_page_name'], action)
-                link = wikiutil.link_tag(request, params, title, request.formatter, rel='nofollow')
+                link = page.link_to(request, text=title, querystr={'action': action}, rel='nofollow')
                 html.append(link)
-                
+
         title = _("DeleteCache", formatted=False)
-        params = '%s?action=%s' % (d['page_name'], 'refresh')
-        link = wikiutil.link_tag(request, params, title, request.formatter, rel='nofollow')
-        
+        link = page.link_to(request, text=title, querystr={'action': 'refresh'}, rel='nofollow')
+
         cache = caching.CacheEntry(request, page, page.getFormatterName(), scope='item')
         date = request.user.getFormattedDateTime(cache.mtime())
         deletecache = u'<p>%s %s</p>' % (link, _('(cached %s)') % date)
--- a/MoinMoin/theme/modern.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/theme/modern.py	Mon Jul 24 22:18:49 2006 +0200
@@ -22,7 +22,7 @@
         html = [
             # Pre header custom html
             self.emit_custom_html(self.cfg.page_header1),
-            
+
             # Header
             u'<div id="header">',
             self.logo(),
@@ -39,10 +39,10 @@
             self.msg(d),
             self.editbar(d),
             u'</div>',
-            
+
             # Post header custom html (not recommended)
             self.emit_custom_html(self.cfg.page_header2),
-            
+
             # Start of page
             self.startPage(),
         ]
@@ -58,16 +58,16 @@
         html = [
             # Pre header custom html
             self.emit_custom_html(self.cfg.page_header1),
-            
+
             # Header
             u'<div id="header">',
             self.title(d),
             self.msg(d),
             u'</div>',
-            
+
             # Post header custom html (not recommended)
             self.emit_custom_html(self.cfg.page_header2),
-            
+
             # Start of page
             self.startPage(),
         ]
@@ -86,23 +86,23 @@
             # End of page
             self.pageinfo(page),
             self.endPage(),
-            
+
             # Pre footer custom html (not recommended!)
             self.emit_custom_html(self.cfg.page_footer1),
-            
+
             # Footer
             u'<div id="footer">',
             self.editbar(d),
             self.credits(d),
             self.showversion(d, **keywords),
             u'</div>',
-            
+
             # Post footer custom html
             self.emit_custom_html(self.cfg.page_footer2),
             ]
         return u'\n'.join(html)
 
-        
+
 def execute(request):
     """
     Generate and return a theme object
--- a/MoinMoin/theme/rightsidebar.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/theme/rightsidebar.py	Mon Jul 24 22:18:49 2006 +0200
@@ -25,7 +25,7 @@
             u'</div>',
             ]
         return u'\n'.join(html)
-    
+
     def pagepanel(self, d):
         """ Create page panel """
         _ = self.request.getText
@@ -37,15 +37,15 @@
                 u'</div>',
                 ]
             return u'\n'.join(html)
-        return ''   
-        
+        return ''
+
     def userpanel(self, d):
         """ Create user panel """
         _ = self.request.getText
 
         html = [
             u'<div class="sidepanel">',
-            u'<h1>%s</h1>' %  _("User"),
+            u'<h1>%s</h1>' % _("User"),
             self.username(d),
             u'</div>'
             ]
@@ -75,7 +75,7 @@
             u'</div>',
             self.trail(d),
             u'</div>',
-            
+
             # Custom html below header (not recomended!)
             self.emit_custom_html(self.cfg.page_header2),
 
@@ -87,12 +87,12 @@
             u'</div>',
 
             self.msg(d),
-            
+
             # Page
             self.startPage(),
             ]
         return u'\n'.join(html)
-    
+
     def editorheader(self, d):
         """
         Assemble page header for editor
@@ -112,7 +112,7 @@
             #self.searchform(d),
             #self.logo(),
             #u'</div>',
-            
+
             # Custom html below header (not recomended!)
             self.emit_custom_html(self.cfg.page_header2),
 
@@ -124,13 +124,13 @@
             u'</div>',
 
             self.msg(d),
-            
+
             # Page
             self.startPage(),
             #self.title(d),
             ]
         return u'\n'.join(html)
-    
+
     def footer(self, d, **keywords):
         """ Assemble wiki footer
         
@@ -144,16 +144,16 @@
             # End of page
             self.pageinfo(page),
             self.endPage(),
-            
+
             # Pre footer custom html (not recommended!)
             self.emit_custom_html(self.cfg.page_footer1),
-            
+
             # Footer
             u'<div id="footer">',
             self.credits(d),
             self.showversion(d, **keywords),
             u'</div>',
-            
+
             # Post footer custom html
             self.emit_custom_html(self.cfg.page_footer2),
             ]
--- a/MoinMoin/user.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/user.py	Mon Jul 24 22:18:49 2006 +0200
@@ -119,7 +119,7 @@
     pwd = '{SHA}' + base64.encodestring(pwd).rstrip()
     return pwd
 
-    
+
 def normalizeName(name):
     """ Make normalized user name
 
@@ -148,7 +148,7 @@
 
     return name
 
-    
+
 def isValidName(request, name):
     """ Validate user name
 
@@ -173,7 +173,7 @@
         if not item:
             continue
         line.append(item)
-        
+
     line = '\t'.join(line)
     return line
 
@@ -193,7 +193,7 @@
         items.append(item)
     return items
 
-    
+
 class User:
     """A MoinMoin User"""
 
@@ -220,7 +220,7 @@
         self.auth_username = auth_username
         self.auth_method = kw.get('auth_method', 'internal')
         self.auth_attribs = kw.get('auth_attribs', ())
-                                       
+
         # create some vars automatically
         self.__dict__.update(self._cfg.user_form_defaults)
 
@@ -254,7 +254,7 @@
         self.editor_default = self._cfg.editor_default
         self.editor_ui = self._cfg.editor_ui
         self.last_saved = str(time.time())
-        
+
         # attrs not saved to profile
         self._request = request
         self._trail = []
@@ -284,7 +284,7 @@
         else:
             from security import Default
             self.may = Default(self)
-        
+
         if self.language and not self.language in i18n.wikiLanguages():
             self.language = 'en'
 
@@ -299,7 +299,7 @@
         # and some other things identifying remote users, then we could also
         # use it reliably in edit locking
         from random import randint
-        return "%s.%d" % (str(time.time()), randint(0,65535))
+        return "%s.%d" % (str(time.time()), randint(0, 65535))
 
     def create_or_update(self, changed=False):
         """ Create or update a user profile
@@ -309,7 +309,7 @@
         if self._cfg.user_autocreate:
             if not self.valid and not self.disabled or changed: # do we need to save/update?
                 self.save() # yes, create/update user profile
-                                
+
     def __filename(self):
         """ Get filename of the user's file on disk
         
@@ -373,7 +373,7 @@
                 return
             # Check for a valid password, possibly changing encoding
             valid, changed = self._validatePassword(user_data)
-            if not valid: 
+            if not valid:
                 return
             else:
                 self.trusted = 1
@@ -395,7 +395,7 @@
             if hasattr(self, attr):
                 delattr(self, attr)
                 changed = 1
-        
+
         # make sure checkboxes are boolean
         for key, label in self._cfg.user_checkbox_fields:
             try:
@@ -458,12 +458,12 @@
 
         # Get the clear text password from the form (require non empty
         # password)
-        password = self._request.form.get('password',[None])[0]
+        password = self._request.form.get('password', [None])[0]
         if not password:
-            return False, False 
-                
+            return False, False
+
         # First get all available pre13 charsets on this system
-        pre13 = ['iso-8859-1', 'iso-8859-2', 'euc-jp', 'gb2312', 'big5',]
+        pre13 = ['iso-8859-1', 'iso-8859-2', 'euc-jp', 'gb2312', 'big5', ]
         available = []
         for charset in pre13:
             try:
@@ -471,7 +471,7 @@
                 available.append(charset)
             except LookupError:
                 pass # missing on this system
-                
+
         # Now try to match the password
         for charset in available:
             # Try to encode, failure is expected
@@ -506,7 +506,7 @@
 
         # !!! should write to a temp file here to avoid race conditions,
         # or even better, use locking
-        
+
         data = codecs.open(self.__filename(), "w", config.charset)
         data.write("# Data saved '%s' for id '%s'\n" % (
             time.strftime(self._cfg.datetime_fmt, time.localtime(time.time())),
@@ -624,7 +624,7 @@
         @return: pages this user has subscribed to
         """
         return self.subscribed_pages
-        
+
     def isSubscribedTo(self, pagelist):
         """ Check if user subscription matches any page in pagelist.
         
@@ -640,15 +640,15 @@
         """
         if not self.valid:
             return False
-        
-        import re        
+
+        import re
         # Create a new list with both names and interwiki names.
         pages = pagelist[:]
         if self._cfg.interwikiname:
             pages += [self._interWikiName(pagename) for pagename in pagelist]
         # Create text for regular expression search
         text = '\n'.join(pages)
-        
+
         for pattern in self.getSubscriptionList():
             # Try simple match first
             if pattern in pages:
@@ -673,15 +673,15 @@
         @type pagename: unicode
         @rtype: bool
         @return: if page was subscribed
-        """        
+        """
         if self._cfg.interwikiname:
             pagename = self._interWikiName(pagename)
-        
+
         if pagename not in self.subscribed_pages:
             self.subscribed_pages.append(pagename)
             self.save()
             return True
-        
+
         return False
 
     def unsubscribe(self, pagename):
@@ -710,16 +710,16 @@
         if pagename in self.subscribed_pages:
             self.subscribed_pages.remove(pagename)
             changed = True
-        
-        interWikiName = self._interWikiName(pagename)        
+
+        interWikiName = self._interWikiName(pagename)
         if interWikiName and interWikiName in self.subscribed_pages:
             self.subscribed_pages.remove(interWikiName)
             changed = True
-    
+
         if changed:
             self.save()
         return not self.isSubscribedTo([pagename])
-        
+
     # -----------------------------------------------------------------
     # Quicklinks
 
@@ -740,14 +740,14 @@
         """
         if not self.valid:
             return False
-            
+
         for pagename in pagelist:
             if pagename in self.quicklinks:
                 return True
             interWikiName = self._interWikiName(pagename)
             if interWikiName and interWikiName in self.quicklinks:
                 return True
-        
+
         return False
 
     def addQuicklink(self, pagename):
@@ -796,8 +796,8 @@
             changed = True
         if pagename in self.quicklinks:
             self.quicklinks.remove(pagename)
-            changed = True        
-        
+            changed = True
+
         if changed:
             self.save()
         return changed
@@ -810,7 +810,7 @@
         """
         if not self._cfg.interwikiname:
             return None
-            
+
         return "%s:%s" % (self._cfg.interwikiname, pagename)
 
     # -----------------------------------------------------------------
@@ -826,7 +826,7 @@
 
         if self.valid and (self.show_page_trail or self.remember_last_visit):
             # load trail if not known
-            self.getTrail()      
+            self.getTrail()
 
             # Add only existing pages that the user may read
             if self._request:
@@ -851,7 +851,7 @@
             self.saveTrail()
 
         # TODO: release lock here
-            
+
     def saveTrail(self):
         """ Save trail file
 
@@ -924,7 +924,7 @@
         """
         if not self.name:
             return self.host()
-        
+
         wikiname, pagename = wikiutil.getInterwikiHomePage(self._request,
                                                            self.name)
         if wikiname == 'Self':
@@ -933,7 +933,7 @@
             else:
                 markup = pagename
         else:
-            markup = '%s:%s' % (wikiname, pagename) 
+            markup = '%s:%s' % (wikiname, pagename)
         return markup
 
     def mailAccountData(self, cleartext_passwd=None):
@@ -944,14 +944,14 @@
         if not self.enc_password: # generate pw if there is none yet
             from random import randint
             import base64
-    
+
             charset = 'utf-8'
             pwd = "%s%d" % (str(time.time()), randint(0, 65535))
             pwd = pwd.encode(charset)
 
             pwd = sha.new(pwd).digest()
             pwd = '{SHA}%s' % base64.encodestring(pwd).rstrip()
-    
+
             self.enc_password = pwd
             self.save()
 
--- a/MoinMoin/util/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -31,7 +31,7 @@
     """
     new_string, num_subst = re.subn(g_undoUtf8Pattern, lambda m: m.group(1), text)
     new_string, num_subst = re.subn(g_cdataCharPattern, lambda m, d=g_charToEntity: d[m.group()], new_string)
-    new_string, num_subst = re.subn(g_xmlIllegalCharPattern, lambda m: '&#x%02X;'%ord(m.group()), new_string)
+    new_string, num_subst = re.subn(g_xmlIllegalCharPattern, lambda m: '&#x%02X;' % ord(m.group()), new_string)
     return new_string
 
 def TranslateText(text):
@@ -41,7 +41,7 @@
     """
     new_string, num_subst = re.subn(g_undoUtf8Pattern, lambda m: m.group(1), text)
     new_string, num_subst = re.subn(g_textCharPattern, lambda m, d=g_charToEntity: d[m.group()], new_string)
-    new_string, num_subst = re.subn(g_xmlIllegalCharPattern, lambda m: '&#x%02X;'%ord(m.group()), new_string)
+    new_string, num_subst = re.subn(g_xmlIllegalCharPattern, lambda m: '&#x%02X;' % ord(m.group()), new_string)
     return new_string
 
 
@@ -79,7 +79,7 @@
     result = '<dt><strong>Form entries</strong></dt>'
     for k in form.keys():
         v = form.get(k, ["<empty>"])
-        v = "|".join(v) 
+        v = "|".join(v)
         result = result + '<dd><em>%s</em>=%s</dd>' % (k, wikiutil.escape(v))
 
     return result
@@ -100,12 +100,12 @@
 
     def __init__(self):
         self.buffer = []
-        
+
     def write(self, foo):
         if not isinstance(foo, unicode):
             foo = foo.decode("iso-8859-1", "replace")
         self.buffer.append(foo)
-    
+
     def getvalue(self):
         return u''.join(self.buffer)
 
--- a/MoinMoin/util/bdiff.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/bdiff.py	Mon Jul 24 22:18:49 2006 +0200
@@ -30,17 +30,17 @@
 
     bin = []
     la = lb = 0
-    
+
     p = [0]
     for i in a: p.append(p[-1] + len(i))
-    
+
     for am, bm, size in difflib.SequenceMatcher(None, a, b).get_matching_blocks():
         s = "".join(b[lb:bm])
         if am > la or s:
             bin.append(struct.pack(BDIFF_PATT, p[la], p[am], len(s)) + s)
         la = am + size
         lb = bm + size
-    
+
     return "".join(bin)
 
 def textdiff(a, b):
@@ -78,12 +78,12 @@
 def test():
     a = ("foo\n" * 30)
     b = ("  fao" * 30)
-    
+
     a = file(r"C:\Dokumente und Einstellungen\Administrator\Eigene Dateien\Progra\Python\MoinMoin\moin-1.6-sync\MoinMoin\util\test.1").read()
     b = file(r"C:\Dokumente und Einstellungen\Administrator\Eigene Dateien\Progra\Python\MoinMoin\moin-1.6-sync\MoinMoin\util\test.2").read()
     a = a.splitlines(1)
     b = b.splitlines(1)
-    
+
     d = diff(a, b)
     z = compress(d)
     print `patchtext(d)`
--- a/MoinMoin/util/chartypes_create.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/chartypes_create.py	Mon Jul 24 22:18:49 2006 +0200
@@ -10,7 +10,7 @@
 lowercase = []
 digits = []
 space = []
-for code in range(1,65535):
+for code in range(1, 65535):
     c = unichr(code)
     str = "\\u%04x" % code
     if c.isupper():
--- a/MoinMoin/util/diff.py	Sat Jul 22 13:38:15 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - Side by side diffs
-
-    @copyright: 2002 by Jürgen Hermann <jh@web.de>
-    @copyright: 2002 by Scott Moonen <smoonen@andstuff.org>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-from MoinMoin.support import difflib
-from MoinMoin.wikiutil import escape
-
-def indent(line):
-    eol = ''
-    while line and line[0] == '\n':
-        eol += '\n'
-        line = line[1:]
-    stripped = line.lstrip()
-    if len(line) - len(stripped):
-        line = "&nbsp;" * (len(line) - len(stripped)) + stripped
-    #return "%d / %d / %s" % (len(line), len(stripped), line)
-    return eol + line
-
-
-# This code originally by Scott Moonen, used with permission.
-def diff(request, old, new):
-    """ Find changes between old and new and return
-        HTML markup visualising them.
-    """
-    _ = request.getText
-    t_line = _("Line") + " %d"
-
-    seq1 = old.splitlines()
-    seq2 = new.splitlines()
-
-    seqobj = difflib.SequenceMatcher(None, seq1, seq2)
-    linematch = seqobj.get_matching_blocks()
-
-    if len(seq1) == len(seq2) and linematch[0] == (0, 0, len(seq1)):
-        # No differences.
-        return _("No differences found!")
-
-    lastmatch = (0, 0)
-
-    result = """
-<table class="diff">
-<tr>
-<td class="diff-removed">
-<span>
-%s
-</span>
-</td>
-<td class="diff-added">
-<span>
-%s
-</span>
-</td>
-</tr>
-""" % (_('Deletions are marked like this.'), _('Additions are marked like this.'),)
-
-    # Print all differences
-    for match in linematch:
-        # Starts of pages identical?
-        if lastmatch == match[0:2]:
-            lastmatch = (match[0] + match[2], match[1] + match[2])
-            continue
-        llineno, rlineno = lastmatch[0]+1, lastmatch[1]+1
-        result += """
-<tr class="diff-title">
-<td>
-%s:
-</td>
-<td>
-%s:
-</td>
-</tr>
-""" % ( request.formatter.line_anchorlink(1, llineno) + request.formatter.text(t_line % llineno) + request.formatter.line_anchorlink(0),
-        request.formatter.line_anchorlink(1, rlineno) + request.formatter.text(t_line % rlineno) + request.formatter.line_anchorlink(0))
-
-        leftpane  = ''
-        rightpane = ''
-        linecount = max(match[0] - lastmatch[0], match[1] - lastmatch[1])
-        for line in range(linecount):
-            if line < match[0] - lastmatch[0]:
-                if line > 0:
-                    leftpane += '\n'
-                leftpane += seq1[lastmatch[0] + line]
-            if line < match[1] - lastmatch[1]:
-                if line > 0:
-                    rightpane += '\n'
-                rightpane += seq2[lastmatch[1] + line]
-
-        charobj   = difflib.SequenceMatcher(None, leftpane, rightpane)
-        charmatch = charobj.get_matching_blocks()
-
-        if charobj.ratio() < 0.5:
-            # Insufficient similarity.
-            if leftpane:
-                leftresult = """<span>%s</span>""" % indent(escape(leftpane))
-            else:
-                leftresult = ''
-
-            if rightpane:
-                rightresult = """<span>%s</span>""" % indent(escape(rightpane))
-            else:
-                rightresult = ''
-        else:
-            # Some similarities; markup changes.
-            charlast = (0, 0)
-
-            leftresult  = ''
-            rightresult = ''
-            for thismatch in charmatch:
-                if thismatch[0] - charlast[0] != 0:
-                    leftresult += """<span>%s</span>""" % indent(
-                        escape(leftpane[charlast[0]:thismatch[0]]))
-                if thismatch[1] - charlast[1] != 0:
-                    rightresult += """<span>%s</span>""" % indent(
-                        escape(rightpane[charlast[1]:thismatch[1]]))
-                leftresult += escape(leftpane[thismatch[0]:thismatch[0] + thismatch[2]])
-                rightresult += escape(rightpane[thismatch[1]:thismatch[1] + thismatch[2]])
-                charlast = (thismatch[0] + thismatch[2], thismatch[1] + thismatch[2])
-
-        leftpane  = '<br>\n'.join(map(indent, leftresult.splitlines()))
-        rightpane = '<br>\n'.join(map(indent, rightresult.splitlines()))
-
-        # removed width="50%%"
-        result += """
-<tr>
-<td class="diff-removed">
-%s
-</td>
-<td class="diff-added">
-%s
-</td>
-</tr>
-""" % (leftpane, rightpane)
-
-        lastmatch = (match[0] + match[2], match[1] + match[2])
-
-    result += '</table>\n'
-    return result
-
--- a/MoinMoin/util/diff3.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/diff3.py	Mon Jul 24 22:18:49 2006 +0200
@@ -25,7 +25,7 @@
     old_nr, other_nr, new_nr = 0, 0, 0
     old_len, other_len, new_len = len(old), len(other), len(new)
     result = []
-    while old_nr < old_len and other_nr < other_len  and new_nr < new_len:
+    while old_nr < old_len and other_nr < other_len and new_nr < new_len:
         # unchanged
         if old[old_nr] == other[other_nr] == new[new_nr]:
             result.append(old[old_nr])
@@ -82,7 +82,7 @@
 
     # process tail
     # all finished
-    if old_nr == old_len and other_nr == other_len  and new_nr == new_len:
+    if old_nr == old_len and other_nr == other_len and new_nr == new_len:
         pass
     # new added lines
     elif old_nr == old_len and other_nr == other_len:
@@ -122,9 +122,9 @@
             if match_len == difference:
                 return (new_match[0], other_match[1]+difference, new_match[1])
             else:
-                other_match =  find_match(old, other,
-                                          other_match[0] + match_len,
-                                          other_match[1] + match_len)
+                other_match = find_match(old, other,
+                                         other_match[0] + match_len,
+                                         other_match[1] + match_len)
         # other changed more lines
         elif difference < 0:
             difference = -difference
@@ -134,14 +134,14 @@
                 return (other_match[0], other_match[1],
                         new_match[0] + difference)
             else:
-                new_match =  find_match(old, new,
-                                        new_match[0] + match_len,
-                                        new_match[1] + match_len)
+                new_match = find_match(old, new,
+                                       new_match[0] + match_len,
+                                       new_match[1] + match_len)
         # both conflicts change same number of lines
         # or no match till the end
         else:
             return (new_match[0], other_match[1], new_match[1])
-        
+
 def match(list1, list2, nr1, nr2, maxcount=3):
     """ return the number matching items after the given positions
         maximum maxcount lines are are processed 
@@ -176,10 +176,10 @@
                 hit1 = (i, idx2)
                 break
             i += 1
-            
+
         i = nr2
         while i < idx2:
-            hit_count = match(list1, list2, idx1, i, mincount) 
+            hit_count = match(list1, list2, idx1, i, mincount)
             if hit_count >= mincount:
                 hit2 = (idx1, i)
                 break
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/util/diff_html.py	Mon Jul 24 22:18:49 2006 +0200
@@ -0,0 +1,143 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Side by side diffs
+
+    @copyright: 2002 by Jürgen Hermann <jh@web.de>
+    @copyright: 2002 by Scott Moonen <smoonen@andstuff.org>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin.support import difflib
+from MoinMoin.wikiutil import escape
+
+def indent(line):
+    eol = ''
+    while line and line[0] == '\n':
+        eol += '\n'
+        line = line[1:]
+    stripped = line.lstrip()
+    if len(line) - len(stripped):
+        line = "&nbsp;" * (len(line) - len(stripped)) + stripped
+    #return "%d / %d / %s" % (len(line), len(stripped), line)
+    return eol + line
+
+
+# This code originally by Scott Moonen, used with permission.
+def diff(request, old, new):
+    """ Find changes between old and new and return
+        HTML markup visualising them.
+    """
+    _ = request.getText
+    t_line = _("Line") + " %d"
+
+    seq1 = old.splitlines()
+    seq2 = new.splitlines()
+
+    seqobj = difflib.SequenceMatcher(None, seq1, seq2)
+    linematch = seqobj.get_matching_blocks()
+
+    if len(seq1) == len(seq2) and linematch[0] == (0, 0, len(seq1)):
+        # No differences.
+        return _("No differences found!")
+
+    lastmatch = (0, 0)
+
+    result = """
+<table class="diff">
+<tr>
+<td class="diff-removed">
+<span>
+%s
+</span>
+</td>
+<td class="diff-added">
+<span>
+%s
+</span>
+</td>
+</tr>
+""" % (_('Deletions are marked like this.'), _('Additions are marked like this.'),)
+
+    # Print all differences
+    for match in linematch:
+        # Starts of pages identical?
+        if lastmatch == match[0:2]:
+            lastmatch = (match[0] + match[2], match[1] + match[2])
+            continue
+        llineno, rlineno = lastmatch[0]+1, lastmatch[1]+1
+        result += """
+<tr class="diff-title">
+<td>
+%s:
+</td>
+<td>
+%s:
+</td>
+</tr>
+""" % (request.formatter.line_anchorlink(1, llineno) + request.formatter.text(t_line % llineno) + request.formatter.line_anchorlink(0),
+       request.formatter.line_anchorlink(1, rlineno) + request.formatter.text(t_line % rlineno) + request.formatter.line_anchorlink(0))
+
+        leftpane = ''
+        rightpane = ''
+        linecount = max(match[0] - lastmatch[0], match[1] - lastmatch[1])
+        for line in range(linecount):
+            if line < match[0] - lastmatch[0]:
+                if line > 0:
+                    leftpane += '\n'
+                leftpane += seq1[lastmatch[0] + line]
+            if line < match[1] - lastmatch[1]:
+                if line > 0:
+                    rightpane += '\n'
+                rightpane += seq2[lastmatch[1] + line]
+
+        charobj = difflib.SequenceMatcher(None, leftpane, rightpane)
+        charmatch = charobj.get_matching_blocks()
+
+        if charobj.ratio() < 0.5:
+            # Insufficient similarity.
+            if leftpane:
+                leftresult = """<span>%s</span>""" % indent(escape(leftpane))
+            else:
+                leftresult = ''
+
+            if rightpane:
+                rightresult = """<span>%s</span>""" % indent(escape(rightpane))
+            else:
+                rightresult = ''
+        else:
+            # Some similarities; markup changes.
+            charlast = (0, 0)
+
+            leftresult = ''
+            rightresult = ''
+            for thismatch in charmatch:
+                if thismatch[0] - charlast[0] != 0:
+                    leftresult += """<span>%s</span>""" % indent(
+                        escape(leftpane[charlast[0]:thismatch[0]]))
+                if thismatch[1] - charlast[1] != 0:
+                    rightresult += """<span>%s</span>""" % indent(
+                        escape(rightpane[charlast[1]:thismatch[1]]))
+                leftresult += escape(leftpane[thismatch[0]:thismatch[0] + thismatch[2]])
+                rightresult += escape(rightpane[thismatch[1]:thismatch[1] + thismatch[2]])
+                charlast = (thismatch[0] + thismatch[2], thismatch[1] + thismatch[2])
+
+        leftpane = '<br>\n'.join(map(indent, leftresult.splitlines()))
+        rightpane = '<br>\n'.join(map(indent, rightresult.splitlines()))
+
+        # removed width="50%%"
+        result += """
+<tr>
+<td class="diff-removed">
+%s
+</td>
+<td class="diff-added">
+%s
+</td>
+</tr>
+""" % (leftpane, rightpane)
+
+        lastmatch = (match[0] + match[2], match[1] + match[2])
+
+    result += '</table>\n'
+    return result
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/util/diff_text.py	Mon Jul 24 22:18:49 2006 +0200
@@ -0,0 +1,78 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - simple text diff (uses difflib)
+
+    @copyright: 2006 by MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin.support import difflib
+
+def diff(oldlines, newlines, **kw):
+    """
+    Find changes between oldlines and newlines.
+    
+    @param oldlines: list of old text lines
+    @param newlines: list of new text lines
+    @keyword ignorews: if 1: ignore whitespace
+    @rtype: list
+    @return: lines like diff tool does output.
+    """
+    false = lambda s: None
+    if kw.get('ignorews', 0):
+        d = difflib.Differ(false)
+    else:
+        d = difflib.Differ(false, false)
+
+    lines = list(d.compare(oldlines, newlines))
+
+    # return empty list if there were no changes
+    changed = 0
+    for l in lines:
+        if l[0] != ' ':
+            changed = 1
+            break
+    if not changed: return []
+
+    if not "we want the unchanged lines, too":
+        if "no questionmark lines":
+            lines = filter(lambda line: line[0] != '?', lines)
+        return lines
+
+
+    # calculate the hunks and remove the unchanged lines between them
+    i = 0              # actual index in lines
+    count = 0          # number of unchanged lines
+    lcount_old = 0     # line count old file
+    lcount_new = 0     # line count new file
+    while i < len(lines):
+        marker = lines[i][0]
+        if marker == ' ':
+            count = count + 1
+            i = i + 1
+            lcount_old = lcount_old + 1
+            lcount_new = lcount_new + 1
+        elif marker in ['-', '+']:
+            if (count == i) and count > 3:
+                lines[:i-3] = []
+                i = 4
+                count = 0
+            elif count > 6:
+                # remove lines and insert new hunk indicator
+                lines[i-count+3:i-3] = ['@@ -%i, +%i @@\n' %
+                                        (lcount_old, lcount_new)]
+                i = i - count + 8
+                count = 0
+            else:
+                count = 0
+                i += 1
+            if marker == '-': lcount_old = lcount_old + 1
+            else: lcount_new = lcount_new + 1
+        elif marker == '?':
+            lines[i:i+1] = []
+
+    # remove unchanged lines a the end
+    if count > 3:
+        lines[-count+3:] = []
+
+    return lines
+
--- a/MoinMoin/util/filesys.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/filesys.py	Mon Jul 24 22:18:49 2006 +0200
@@ -109,7 +109,7 @@
     """
     names = os.listdir(src)
     os.mkdir(dst)
-    copystat(src,dst)
+    copystat(src, dst)
     errors = []
     for name in names:
         srcname = os.path.join(src, name)
@@ -167,3 +167,4 @@
 
     def realPathCase(path):
         return None
+
--- a/MoinMoin/util/lock.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/lock.py	Mon Jul 24 22:18:49 2006 +0200
@@ -23,7 +23,7 @@
     """
     defaultSleep = 0.25
     maxSleep = 0.25
-    
+
     def __init__(self, timeout):
         self.setTimeout(timeout)
         self._start = None
@@ -35,7 +35,7 @@
             self._sleep = self.defaultSleep
         else:
             self._sleep = min(timeout / 10.0, self.maxSleep)
-        
+
     def start(self):
         """ Start the countdown """
         if self.timeout is None:
@@ -53,7 +53,7 @@
     def sleep(self):
         """ Sleep without sleeping over timeout """
         if self._stop is not None:
-            timeLeft = max(self._stop - time.time(), 0)            
+            timeLeft = max(self._stop - time.time(), 0)
             sleep = min(self._sleep, timeLeft)
         else:
             sleep = self._sleep
@@ -62,7 +62,7 @@
     def elapsed(self):
         return time.time() - self._start
 
-    
+
 class ExclusiveLock:
     """ Exclusive lock
     
@@ -77,7 +77,7 @@
     """
     fileName = '' # The directory is the lockDir
     timerClass = Timer
-    
+
     def __init__(self, dir, timeout=None):
         """ Init a write lock
         
@@ -96,7 +96,7 @@
             self.lockDir = os.path.join(dir, self.fileName)
             self._makeDir()
         else:
-            self.lockDir = dir            
+            self.lockDir = dir
         self._locked = False
 
     def acquire(self, timeout=None):
@@ -165,7 +165,7 @@
         return False
 
     # Private -------------------------------------------------------
-    
+
     def _makeDir(self):
         """ Make sure directory exists """
         try:
@@ -180,7 +180,7 @@
         try:
             os.rmdir(self.lockDir)
         except OSError, err:
-            if err.errno != errno.ENOENT: 
+            if err.errno != errno.ENOENT:
                 raise
 
 
@@ -195,7 +195,7 @@
     lock will try to expire all existing ReadLocks.
     """
     fileName = 'write_lock'
-    
+
     def __init__(self, dir, timeout=None, readlocktimeout=None):
         """ Init a write lock
         
@@ -211,7 +211,7 @@
             self.readlocktimeout = timeout
         else:
             self.readlocktimeout = readlocktimeout
-    
+
     def acquire(self, timeout=None):
         """ Acquire an exclusive write lock
         
@@ -221,7 +221,7 @@
         acquired.
         
         Return True if lock acquired, False otherwise.
-        """        
+        """
         if self._locked:
             raise RuntimeError("lock already locked")
         result = False
@@ -242,9 +242,9 @@
                 else:
                     self.release()
         return False
-        
+
     # Private -------------------------------------------------------
-    
+
     def _expireReadLocks(self):
         """ Expire old read locks """
         readLockFileName = ReadLock.fileName
@@ -255,7 +255,7 @@
             ExclusiveLock(LockDir, self.readlocktimeout).expire()
 
     def _haveReadLocks(self):
-        """ Return True if read locks exists; False otherwise """            
+        """ Return True if read locks exists; False otherwise """
         readLockFileName = ReadLock.fileName
         for name in os.listdir(self.dir):
             if name.startswith(readLockFileName):
@@ -273,7 +273,7 @@
     Allows only one lock per instance.
     """
     fileName = 'read_lock_'
-            
+
     def __init__(self, dir, timeout=None):
         """ Init a read lock
         
@@ -302,6 +302,6 @@
                 # log('acquired read lock: %s\n' % self.lockDir)
                 return True
             finally:
-                self.writeLock.release()       
+                self.writeLock.release()
         return False
 
--- a/MoinMoin/util/profile.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/profile.py	Mon Jul 24 22:18:49 2006 +0200
@@ -64,7 +64,7 @@
         self.count = 0 # count between somples
         self.requests = 0 # requests added
         self.data = {'collect': 'NA'} # Sample data
-        
+
     def addRequest(self):
         """ Add a request to the profile
 
@@ -76,12 +76,12 @@
         Invoke sample when self.count reach self.requestsPerSample.
         """
         self.requests += 1
-        self.count += 1 
+        self.count += 1
         if self.count == self.requestsPerSample:
             # Time for a sample
             self.count = 0
             self.sample()
-    
+
     def sample(self):
         """ Make a sample of memory usage and log it
 
@@ -90,13 +90,13 @@
         
         Invoke common methods for all profilers. Some profilers like
         TwistedProfiler override this method. 
-        """        
+        """
         self._setData()
         self._setMemory()
         self._log()
-    
+
     # Private methods ------------------------------------------------------
-    
+
     def _setData(self):
         """ Collect sample data into self.data
 
@@ -109,7 +109,7 @@
             d['collect'] = str(gc.collect())
         d['objects'] = len(gc.get_objects())
         d['garbage'] = len(gc.garbage)
-                     
+
     def _setMemory(self):
         """ Get process memory usage
 
@@ -120,14 +120,14 @@
         """
         lines = os.popen('/bin/ps -p %s -o rss' % self.pid).readlines()
         self.data['memory'] = lines[1].strip()
-    
+
     def _log(self):
         """ Format sample and write to log
 
         Private method used by profilers.
         """
         line = ('%(date)s req:%(requests)d mem:%(memory)sKB collect:%(collect)s '
-                'objects:%(objects)d garbage:%(garbage)d\n' % self.data) 
+                'objects:%(objects)d garbage:%(garbage)d\n' % self.data)
         self.logfile.write(line)
         self.logfile.flush()
 
@@ -145,10 +145,10 @@
         Invoke Profiler.__init__ and import getProcessOuput from
         twisted.
         """
-        Profiler.__init__(self, name, requestsPerSample, collect) 
+        Profiler.__init__(self, name, requestsPerSample, collect)
         from twisted.internet.utils import getProcessOutput
-        self._getProcessOutput = getProcessOutput        
-        
+        self._getProcessOutput = getProcessOutput
+
     def sample(self):
         """ Make a sample of memory usage and log it
         
@@ -161,23 +161,22 @@
         """
         self._setData()
         # Memory will be available little later
-        deferred = self._getProcessOutput('/bin/ps', 
+        deferred = self._getProcessOutput('/bin/ps',
                                           ('-p', str(self.pid), '-o', 'rss'))
         deferred.addCallback(self._callback)
 
     # Private methods ------------------------------------------------------
-    
-    def _callback(self, data):        
+
+    def _callback(self, data):
         """ Called from deferred when ps output is available
 
         Private method, don't call this.
         """
         self.data['memory'] = data.split('\n')[1].strip()
-        self._log()  
-    
-    
+        self._log()
+
+
 if __name__ == '__main__':
     # In case someone try to run as a script
     print __doc__
-    
 
--- a/MoinMoin/util/pysupport.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/pysupport.py	Mon Jul 24 22:18:49 2006 +0200
@@ -30,7 +30,7 @@
     import os, re
 
     packagedir = os.path.dirname(packagefile)
-    
+
     in_plugin_dir = lambda dir, ops=os.path.split: ops(ops(dir)[0])[1] == "plugin"
 
     moinmodule = __import__('MoinMoin')
@@ -85,12 +85,12 @@
     if lock is None:
         import threading
         lock = threading.Lock()
-    
+
     def decorated(*args, **kw):
         lock.acquire()
         try:
             return function(*args, **kw)
         finally:
             lock.release()
-            
+
     return decorated
--- a/MoinMoin/util/thread_monitor.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/util/thread_monitor.py	Mon Jul 24 22:18:49 2006 +0200
@@ -41,7 +41,7 @@
 
 def dump_hook(a, b, c): # arguments are ignored
     global dumping
-    
+
     if dumping and sys.exc_info()[0] is None:
         thread = threading.currentThread()
         if thread in to_dump:
@@ -65,7 +65,7 @@
     """ Activates the thread monitor hook. Note that this interferes
     with any kind of profiler and some debugging extensions. """
     global hook_enabled
-    
+
     sys.setprofile(dump_hook)
     threading.setprofile(dump_hook)
     hook_enabled = True
@@ -78,6 +78,6 @@
         while 1:
             sleep(seconds)
             trigger_dump()
-    
+
     threading.Thread(target=background_dumper, args=(seconds, )).start()
 
--- a/MoinMoin/widget/base.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/widget/base.py	Mon Jul 24 22:18:49 2006 +0200
@@ -12,5 +12,5 @@
         self.request = request
 
     def render(self):
-        raise NotImplementedError 
+        raise NotImplementedError
 
--- a/MoinMoin/widget/html.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/widget/html.py	Mon Jul 24 22:18:49 2006 +0200
@@ -26,7 +26,7 @@
     def __unicode__(self):
         return wikiutil.escape(self.text)
 
-        
+
 class Raw:
     """ Raw HTML code.
     """
@@ -36,7 +36,7 @@
     def __unicode__(self):
         return self.markup
 
-        
+
 class Element:
     """ Abstract base class for HTML elements.
     """
@@ -86,9 +86,9 @@
         return ' '.join(result)
 
     def __unicode__(self):
-        raise NotImplementedError 
+        raise NotImplementedError
 
-        
+
 class EmptyElement(Element):
     """ HTML elements with an empty content model.
     """
--- a/MoinMoin/wikiutil.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/wikiutil.py	Mon Jul 24 22:18:49 2006 +0200
@@ -9,7 +9,6 @@
 import os, re, urllib, cgi
 import codecs, types
 
-from MoinMoin.support import difflib
 from MoinMoin import util, version, config
 from MoinMoin.util import pysupport, filesys
 
@@ -1449,76 +1448,6 @@
     """ Returns true if there is a conflict marker in the text. """
     return "/!\ '''Edit conflict" in text
 
-def linediff(oldlines, newlines, **kw):
-    """
-    Find changes between oldlines and newlines.
-    
-    @param oldlines: list of old text lines
-    @param newlines: list of new text lines
-    @keyword ignorews: if 1: ignore whitespace
-    @rtype: list
-    @return: lines like diff tool does output.
-    """
-    false = lambda s: None
-    if kw.get('ignorews', 0):
-        d = difflib.Differ(false)
-    else:
-        d = difflib.Differ(false, false)
-
-    lines = list(d.compare(oldlines, newlines))
-
-    # return empty list if there were no changes
-    changed = 0
-    for l in lines:
-        if l[0] != ' ':
-            changed = 1
-            break
-    if not changed: return []
-
-    if not "we want the unchanged lines, too":
-        if "no questionmark lines":
-            lines = filter(lambda line: line[0] != '?', lines)
-        return lines
-
-
-    # calculate the hunks and remove the unchanged lines between them
-    i = 0              # actual index in lines
-    count = 0          # number of unchanged lines
-    lcount_old = 0     # line count old file
-    lcount_new = 0     # line count new file
-    while i < len(lines):
-        marker = lines[i][0]
-        if marker == ' ':
-            count = count + 1
-            i = i + 1
-            lcount_old = lcount_old + 1
-            lcount_new = lcount_new + 1
-        elif marker in ['-', '+']:
-            if (count == i) and count > 3:
-                lines[:i-3] = []
-                i = 4
-                count = 0
-            elif count > 6:
-                # remove lines and insert new hunk indicator
-                lines[i-count+3:i-3] = ['@@ -%i, +%i @@\n' %
-                                        (lcount_old, lcount_new)]
-                i = i - count + 8
-                count = 0
-            else:
-                count = 0
-                i += 1
-            if marker == '-': lcount_old = lcount_old + 1
-            else: lcount_new = lcount_new + 1
-        elif marker == '?':
-            lines[i:i+1] = []
-
-    # remove unchanged lines a the end
-    if count > 3:
-        lines[-count+3:] = []
-
-    return lines
-
-
 def pagediff(request, pagename1, rev1, pagename2, rev2, **kw):
     """
     Calculate the "diff" between two page contents.
@@ -1532,10 +1461,11 @@
     @return: lines of diff output
     """
     from MoinMoin.Page import Page
+    from MoinMoin.util import diff_text
     lines1 = Page(request, pagename1, rev=rev1).getlines()
     lines2 = Page(request, pagename2, rev=rev2).getlines()
 
-    lines = linediff(lines1, lines2, **kw)
+    lines = diff_text.diff(lines1, lines2, **kw)
     return lines
 
 
--- a/MoinMoin/wikixml/marshal.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/wikixml/marshal.py	Mon Jul 24 22:18:49 2006 +0200
@@ -34,7 +34,7 @@
     # Container Tags
     ROOT_CONTAINER = "data"
     ITEM_CONTAINER = "item"
-    
+
     # List of private prefixes
     PRIVATE_PREFIXES = ['_']
 
@@ -42,7 +42,7 @@
     TAG_MAP = {}
 
 
-    def __toXML(self, element, data): 
+    def __toXML(self, element, data):
         """ Recursive helper method that transforms an object to XML.
         
             Returns a list of strings, which constitute the XML document.
--- a/MoinMoin/xmlrpc/ProcessMail.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/xmlrpc/ProcessMail.py	Mon Jul 24 22:18:49 2006 +0200
@@ -12,13 +12,13 @@
     request = xmlrpcobj.request
     secret = xmlrpcobj._instr(secret)
     mail = str(mail)
-    
+
     if not request.cfg.mail_import_secret:
         return u"No password set"
-    
+
     if request.cfg.mail_import_secret != secret:
         return u"Invalid password"
-    
+
     try:
         mailimport.import_mail_from_string(request, mail)
     except mailimport.ProcessingError, e:
--- a/MoinMoin/xmlrpc/UpdateGroup.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/xmlrpc/UpdateGroup.py	Mon Jul 24 22:18:49 2006 +0200
@@ -34,7 +34,7 @@
     # change your wikiconfig to have xmlrpc_putpage_trusted_only = 0
     # and make very very sure that nobody untrusted can access your wiki
     # via network or somebody will raid your wiki some day!
-        
+
     if self.request.cfg.xmlrpc_putpage_trusted_only and not self.request.user.trusted:
         return xmlrpclib.Fault(1, "You are not allowed to edit this page")
 
@@ -45,7 +45,7 @@
     # check if groupname matches page_group_regex
     if not re.match(self.request.cfg.page_group_regex, groupname):
         return xmlrpclib.Fault(2, "The groupname %s does not match your page_group_regex (%s)" % (
-	                          groupname, self.request.cfg.page_group_regex))
+                               groupname, self.request.cfg.page_group_regex))
 
     newtext = """\
 #acl %(acl)s
@@ -56,7 +56,7 @@
     'comment': groupcomment,
     'memberlist': "\n * ".join([''] + memberlist)
     }
-    
+
     page = PageEditor(self.request, pagename)
     try:
         msg = page.saveText(newtext, 0)
--- a/MoinMoin/xmlrpc/__init__.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/MoinMoin/xmlrpc/__init__.py	Mon Jul 24 22:18:49 2006 +0200
@@ -59,7 +59,7 @@
         @return: string in config.charset
         """
         raise "NotImplementedError"
-    
+
     def _outstr(self, text):
         """ Convert outbound string to utf-8.
 
@@ -68,7 +68,7 @@
         @return: string in utf-8
         """
         raise "NotImplementedError"
-    
+
     def _inlob(self, text):
         """ Convert inbound base64-encoded utf-8 to Large OBject.
         
@@ -93,7 +93,7 @@
             if config.charset != 'utf-8':
                 text = unicode(text, config.charset).encode('utf-8')
         return xmlrpclib.Binary(text)
-                    
+
     def _dump_exc(self):
         """ Convert an exception to a string.
         
@@ -113,13 +113,13 @@
         try:
             data = self.request.read()
             params, method = xmlrpclib.loads(data)
-    
+
             if _debug:
                 sys.stderr.write('- XMLRPC ' + '-' * 70 + '\n')
                 sys.stderr.write('%s(%s)\n\n' % (method, repr(params)))
-            
+
             response = self.dispatch(method, params)
-            
+
         except:
             # report exception back to server
             response = xmlrpclib.dumps(xmlrpclib.Fault(1, self._dump_exc()))
@@ -142,7 +142,7 @@
 
     def dispatch(self, method, params):
         method = method.replace(".", "_")
-        
+
         try:
             fn = getattr(self, 'xmlrpc_' + method)
         except AttributeError:
@@ -156,16 +156,16 @@
                 response = fn(self, *params)
         else:
             response = fn(*params)
-        
+
         return response
 
     # Common faults -----------------------------------------------------
-    
+
     def notAllowedFault(self):
         return xmlrpclib.Fault(1, "You are not allowed to read this page.")
 
     def noSuchPageFault(self):
-        return xmlrpclib.Fault(1, "No such page was found.")        
+        return xmlrpclib.Fault(1, "No such page was found.")
 
     #############################################################################
     ### System methods
@@ -193,13 +193,13 @@
                 results.append([self.dispatch(method_name, params)])
             except xmlrpclib.Fault, fault:
                 results.append(
-                    {'faultCode' : fault.faultCode,
-                     'faultString' : fault.faultString}
+                    {'faultCode': fault.faultCode,
+                     'faultString': fault.faultString}
                     )
             except:
                 results.append(
-                    {'faultCode' : 1,
-                     'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)}
+                    {'faultCode': 1,
+                     'faultString': "%s:%s" % (sys.exc_type, sys.exc_value)}
                     )
         return results
 
@@ -269,9 +269,9 @@
             * version (int) :
                 Current version.
         """
-        
+
         return_items = []
-        
+
         edit_log = editlog.EditLog(self.request)
         for log in edit_log.reverse():
             # get last-modified UTC (DateTime) from log
@@ -281,11 +281,11 @@
             # skip if older than "date"
             if lastModified_date < date:
                 break
-            
+
             # skip if knowledge not permitted
             if not self.request.user.may.read(log.pagename):
                 continue
-            
+
             # get page name (str) from log
             pagename_str = self._outstr(log.pagename)
 
@@ -297,12 +297,12 @@
                     author_str = userdata.name
             author_str = self._outstr(author_str)
 
-            return_item = { 'name':  pagename_str,
-                            'lastModified': lastModified_date,
-                            'author': author_str,
-                            'version': int(log.rev) }
+            return_item = {'name': pagename_str,
+                           'lastModified': lastModified_date,
+                           'author': author_str,
+                           'version': int(log.rev) }
             return_items.append(return_item)
-        
+
         return return_items
 
     def xmlrpc_getPageInfo(self, pagename):
@@ -328,7 +328,7 @@
         if not self.request.user.may.read(pn):
             return self.notAllowedFault()
 
-        if rev != None:
+        if rev is not None:
             page = Page(self.request, pn, rev=rev)
         else:
             page = Page(self.request, pn)
@@ -339,10 +339,10 @@
             return self.noSuchPageFault()
 
         # Get page info
-        last_edit = page.last_edit(self.request)           
+        last_edit = page.last_edit(self.request)
         mtime = wikiutil.version2timestamp(long(last_edit['timestamp'])) # must be long for py 2.2.x
         gmtuple = tuple(time.gmtime(mtime))
-        
+
         version = rev # our new rev numbers: 1,2,3,4,....
 
         #######################################################################
@@ -355,10 +355,10 @@
         if self.request.cfg.sitename == 'MoinMaster' and pagename == 'BadContent':
             version = int(mtime)
         #######################################################################
-            
+
         return {
             'name': self._outstr(page.page_name),
-            'lastModified' : xmlrpclib.DateTime(gmtuple),
+            'lastModified': xmlrpclib.DateTime(gmtuple),
             'author': self._outstr(last_edit['editor']),
             'version': version,
             }
@@ -374,14 +374,14 @@
         @param rev: revision number (int)
         @rtype: str
         @return: utf-8 encoded page data
-        """    
+        """
         pagename = self._instr(pagename)
 
         # User may read page?
         if not self.request.user.may.read(pagename):
             return self.notAllowedFault()
 
-        if rev != None:
+        if rev is not None:
             page = Page(self.request, pagename, rev=rev)
         else:
             page = Page(self.request, pagename)
@@ -414,7 +414,7 @@
         if not self.request.user.may.read(pagename):
             return self.notAllowedFault()
 
-        if rev != None:
+        if rev is not None:
             page = Page(self.request, pagename, rev=rev)
         else:
             page = Page(self.request, pagename)
@@ -422,11 +422,11 @@
         # Non existing page?
         if not page.exists():
             return self.noSuchPageFault()
-        
+
         # Render page into a buffer
         result = self.request.redirectedOutput(page.send_page, self.request,
                                                content_only=1)
-        
+
         # Return rendered page
         if self.version == 2:
             return self._outstr(result)
@@ -454,10 +454,10 @@
         # Non existing page?
         if not page.exists():
             return self.noSuchPageFault()
-        
+
         links_out = []
         for link in page.getPageLinks(self.request):
-            links_out.append({ 'name': self._outstr(link), 'type': 0 })
+            links_out.append({'name': self._outstr(link), 'type': 0 })
         return links_out
 
     def xmlrpc_putPage(self, pagename, pagetext):
@@ -469,10 +469,10 @@
         @return: true on success
         """
         # READ THIS OR IT WILL NOT WORK ===================================
-        
+
         # we use a test page instead of using the requested pagename, if
         # xmlrpc_putpage_enabled was not set in wikiconfig.
-        
+
         if self.request.cfg.xmlrpc_putpage_enabled:
             pagename = self._instr(pagename)
         else:
@@ -484,7 +484,7 @@
         # change your wikiconfig to have xmlrpc_putpage_trusted_only = 0
         # and make very very sure that nobody untrusted can access your wiki
         # via network or somebody will raid your wiki some day!
-        
+
         if self.request.cfg.xmlrpc_putpage_trusted_only and not self.request.user.trusted:
             return xmlrpclib.Fault(1, "You are not allowed to edit this page")
 
@@ -529,7 +529,7 @@
 
 
     # authorization methods
-    
+
     def xmlrpc_getAuthToken(self, username, password, *args):
         """ Returns a token which can be used for authentication
             in other XMLRPC calls. If the token is empty, the username
@@ -539,7 +539,7 @@
             return u.id
         else:
             return ""
-    
+
     def xmlrpc_applyAuthToken(self, auth_token):
         """ Applies the auth token and thereby authenticates the user. """
         u = user.User(self.request, id=auth_token, auth_method='xmlrpc_applytoken')
@@ -555,7 +555,7 @@
     def xmlrpc_getDiff(self, pagename, from_rev, to_rev):
         """ Gets the binary difference between two page revisions. See MoinMoin:WikiSyncronisation. """
         from MoinMoin.util.bdiff import textdiff, compress
-        
+
         pagename = self._instr(pagename)
 
         # User may read page?
@@ -569,44 +569,44 @@
 
         if not allowed_rev_type(from_rev):
             return xmlrpclib.Fault("FROMREV_INVALID", "Incorrect type for from_rev.")
-        
+
         if not allowed_rev_type(to_rev):
             return xmlrpclib.Fault("TOREV_INVALID", "Incorrect type for to_rev.")
-        
+
         currentpage = Page(self.request, pagename)
         if not currentpage.exists():
             return xmlrpclib.Fault("NOT_EXIST", "Page does not exist.")
-        
+
         revisions = currentpage.getRevList()
-        
+
         if from_rev is not None and from_rev not in revisions:
             return xmlrpclib.Fault("FROMREV_INVALID", "Unknown from_rev.")
         if to_rev is not None and to_rev not in revisions:
             return xmlrpclib.Fault("TOREV_INVALID", "Unknown to_rev.")
-        
+
         # use lambda to defer execution in the next lines
         if from_rev is None:
             oldcontents = lambda: ""
         else:
             oldpage = Page(request, pagename, rev=from_rev)
             oldcontents = lambda: oldpage.get_raw_body_str()
-        
+
         if to_rev is None:
             newcontents = lambda: currentpage.get_raw_body()
         else:
             newpage = Page(request, pagename, rev=to_rev)
             newcontents = lambda: newpage.get_raw_body_str()
             newrev = newpage.get_real_rev()
-        
+
         if oldcontents() and oldpage.get_real_rev() == newpage.get_real_rev():
             return xmlrpclib.Fault("ALREADY_CURRENT", "There are no changes.")
-        
+
         newcontents = newcontents()
         conflict = wikiutil.containsConflictMarker(newcontents)
         diffblob = xmlrpclib.Binary(compress(textdiff(oldcontents(), newcontents)))
-        
+
         return {"conflict": conflict, "diff": diffblob, "diffversion": 1, "current": currentpage.get_real_rev()}
-    
+
     def xmlrpc_interwikiName(self):
         """ Returns the interwiki name of the current wiki. """
         name = self.request.cfg.interwikiname
@@ -614,7 +614,7 @@
             return None
         else:
             return self._outstr(name)
-    
+
     def xmlrpc_mergeChanges(self, pagename, diff, local_rev, delta_remote_rev, last_remote_rev, interwiki_name):
         """ Merges a diff sent by the remote machine and returns the number of the new revision.
             Additionally, this method tags the new revision.
@@ -629,9 +629,9 @@
         from MoinMoin.util.bdiff import decompress, patch
         from MoinMoin.wikisync import TagStore
         LASTREV_INVALID = xmlrpclib.Fault("LASTREV_INVALID", "The page was changed")
-        
+
         pagename = self._instr(pagename)
-       
+
         comment = u"Remote - %r" % interwiki_name
         
         # User may read page?
@@ -639,22 +639,22 @@
             return self.notAllowedFault()
 
         # XXX add locking here!
-        
+
         # current version of the page
         currentpage = PageEditor(self.request, pagename, do_editor_backup=0)
 
         if currentpage.get_real_rev() != last_remote_rev:
             return LASTREV_INVALID
-        
+
         if not currentpage.exists() and diff is None:
             return xmlrpclib.Fault("NOT_EXIST", "The page does not exist and no diff was supplied.")
-        
+
         # base revision used for the diff
         basepage = Page(self.request, pagename, rev=delta_remote_rev)
-        
+
         # generate the new page revision by applying the diff
         newcontents = patch(basepage.get_raw_body_str(), decompress(str(diff)))
-        
+
         # write page
         try:
             currentpage.saveText(newcontents.encode("utf-8"), last_remote_rev, comment=comment)
@@ -686,12 +686,12 @@
         @param pagename: pagename (utf-8)
         @rtype: list
         @return: a list of utf-8 attachment names
-        """    
+        """
         pagename = self._instr(pagename)
         # User may read page?
         if not self.request.user.may.read(pagename):
             return self.notAllowedFault()
-        
+
         result = AttachFile._get_files(self.request, pagename)
         return result
 
@@ -735,7 +735,7 @@
         # also check ACLs
         if not self.request.user.may.write(pagename):
             return xmlrpclib.Fault(1, "You are not allowed to edit this page")
-        
+
         attachname = wikiutil.taintfilename(attachname)
         filename = AttachFile.getFilename(self.request, pagename, attachname)
         if os.path.exists(filename) and not os.path.isfile(filename):
@@ -744,12 +744,12 @@
         os.chmod(filename, 0666 & config.umask)
         AttachFile._addLogEntry(self.request, 'ATTNEW', pagename, filename)
         return xmlrpclib.Boolean(1)
-    
+
     # XXX END WARNING XXX
 
 
 class XmlRpc1(XmlRpcBase):
-    
+
     def __init__(self, request):
         XmlRpcBase.__init__(self, request)
         self.version = 1
@@ -772,9 +772,9 @@
         """
         return wikiutil.url_quote(text) # config.charset must be utf-8
 
-    
+
 class XmlRpc2(XmlRpcBase):
-    
+
     def __init__(self, request):
         XmlRpcBase.__init__(self, request)
         self.version = 2
@@ -798,9 +798,9 @@
         @return: text encoded in utf-8
         """
         if isinstance(text, unicode):
-            text = text.encode('utf-8')           
-        elif config.charset != 'utf-8':        
-            text = unicode(text, config.charset).encode('utf-8')               
+            text = text.encode('utf-8')
+        elif config.charset != 'utf-8':
+            text = unicode(text, config.charset).encode('utf-8')
         return text
 
 
--- a/docs/CHANGES	Sat Jul 22 13:38:15 2006 +0200
+++ b/docs/CHANGES	Mon Jul 24 22:18:49 2006 +0200
@@ -67,6 +67,8 @@
     * refactored some actions to use ActionBase base class
     * moved "test" action from wikiaction to MoinMoin/action/
       (and use ActionBase)
+    * moved MoinMoin/config.py to MoinMoin/config/__init__.py
+    * moved MoinMoin/multiconfig.py to MoinMoin/config/multiconfig.py
     * moved "SystemInfo" macro from wikimacro to MoinMoin/macro/
     * moved wikiaction.py stuff to MoinMoin/action/__init__.py
     * moved wikimacro.py stuff to MoinMoin/macro/__init__.py
@@ -116,6 +118,7 @@
     * removed all _ magic in URLs and filenames
       TODO: write mig script for data_dir
       TODO: make blanks in interwiki pagelinks possible
+    * request.action now has the action requested, default: 'show'.
 
   New Features:
     * Removed "underscore in URL" == "blank in pagename magic" - it made more
@@ -189,6 +192,9 @@
       internally, too. So if GUI editor invocation is broken due to browser
       compatibility issues or a wrong browser version check, please file a bug
       at FCKeditor development or browser development.
+    * HINT: instead of "from MoinMoin.multiconfig import DefaultConfig" you
+      need to use "from MoinMoin.config.multiconfig import DefaultConfig" now.
+      You need to change this in you wikiconfig.py or farmconfig.py file.
 
 Version 1.5.4-current:
     * increased maxlength of some input fields from 80 to 200
--- a/wiki/config/more_samples/ldap_smb_farmconfig.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/wiki/config/more_samples/ldap_smb_farmconfig.py	Mon Jul 24 22:18:49 2006 +0200
@@ -69,7 +69,7 @@
 # this is to get everything to sane defaults, so we need to change only what
 # we like to have different:
 
-from MoinMoin.multiconfig import DefaultConfig
+from MoinMoin.config.multiconfig import DefaultConfig
 
 # Now we subclass this DefaultConfig. This means that we inherit every setting
 # from the DefaultConfig, except those we explicitely define different.
--- a/wiki/config/wikiconfig.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/wiki/config/wikiconfig.py	Mon Jul 24 22:18:49 2006 +0200
@@ -15,7 +15,7 @@
 
     Note that there are more config options than you'll find in
     the version of this file that is installed by default; see
-    the module MoinMoin.multiconfig for a full list of names and their
+    the module MoinMoin.config.multiconfig for a full list of names and their
     default values.
 
     Also, the URL http://moinmoin.wikiwikiweb.de/HelpOnConfiguration has
@@ -25,7 +25,7 @@
     from the wikifarm directory instead! **
 """
 
-from MoinMoin.multiconfig import DefaultConfig
+from MoinMoin.config.multiconfig import DefaultConfig
 
 
 class Config(DefaultConfig):
--- a/wiki/config/wikifarm/farmconfig.py	Sat Jul 22 13:38:15 2006 +0200
+++ b/wiki/config/wikifarm/farmconfig.py	Mon Jul 24 22:18:49 2006 +0200
@@ -14,7 +14,7 @@
 
     Note that there are more config options than you'll find in
     the version of this file that is installed by default; see
-    the module MoinMoin.multiconfig for a full list of names and their
+    the module MoinMoin.config.multiconfig for a full list of names and their
     default values.
 
     Also, the URL http://moinmoin.wikiwikiweb.de/HelpOnConfiguration has
@@ -42,10 +42,10 @@
 wikis = [
     # Standalone server needs the port e.g. localhost:8000
     # Twisted server can now use the port, too.
-    
+
     # wikiname,     url regular expression (no protocol)
     # ---------------------------------------------------------------
-    ("mywiki",  r".*"),   # this is ok for a single wiki
+    ("mywiki", r".*"),   # this is ok for a single wiki
 
     # for multiple wikis, do something like this:
     #("moinmoin",    r"^moinmoin.wikiwikiweb.de/.*$"),
@@ -65,7 +65,7 @@
 # this is to get everything to sane defaults, so we need to change only what
 # we like to have different:
 
-from MoinMoin.multiconfig import DefaultConfig
+from MoinMoin.config.multiconfig import DefaultConfig
 
 # Now we subclass this DefaultConfig. This means that we inherit every setting
 # from the DefaultConfig, except those we explicitely define different.