changeset 4677:9eeae87fdd36

merged moin/1.7
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Mon, 06 Apr 2009 12:28:51 +0200
parents 0946442e94ab (diff) e42fc41f71a4 (current diff)
children e17387246649 58b18877d6ac
files MoinMoin/auth/ldap_login.py
diffstat 1289 files changed, 171778 insertions(+), 139533 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Mon Apr 06 12:27:11 2009 +0200
+++ b/.hgtags	Mon Apr 06 12:28:51 2009 +0200
@@ -22,6 +22,14 @@
 9901ffff5280b81d0476e8d1e434ea70e3a6fbdc 1.7.0rc2
 01ef230fb671b0f0636328b04ade9b44c5548327 1.7.0rc3
 761c3a503be2b97d8e7beb902751dbb5e60f3127 1.7.0
+b1e192a3651a57aa451fefca06f5a849e1e4b422 SOC2008-END
 da9e664b3f518f0cab89f7ebf22b99ecac0eaaa1 1.7.1
 ae9bf455eec6a080dd6aafacdaf30fb881b1ca68 1.7.2
 ffdb44bf3b7391f660735fd509015819e8feaad2 1.7.3
+2907390f9d4630f5459506d3d3352c96f75e67b3 1.8.0beta1
+117a21659358defd880baaccf5c73e7b1a71ea1e 1.8.0beta2
+df1ba1ddf05061d6451797869c159b3c60319470 1.8.0beta3
+4186e90ead069aad4b739f278eb8599dc00ff6a4 1.8.0rc1
+7fee549f435d119801075f0d18fc57822f78f0eb 1.8.0
+fad2936d33a7eac9370f3cdc28f0ec375518d5cf 1.8.1
+1f0db10c207f697de8d496f298167d7de76f30ac 1.8.2
--- a/Makefile	Mon Apr 06 12:27:11 2009 +0200
+++ b/Makefile	Mon Apr 06 12:28:51 2009 +0200
@@ -3,7 +3,7 @@
 #
 
 # location for the wikiconfig.py we use for testing:
-export PYTHONPATH=$(PWD)/tests:$(PWD)
+export PYTHONPATH=$(PWD)
 
 testwiki := ./tests/wiki
 share := ./wiki
@@ -13,9 +13,9 @@
 
 install-docs:
 	-mkdir build
-	wget -U MoinMoin/Makefile -O build/INSTALL.html "http://master17.moinmo.in/MoinMoin/InstallDocs?action=print"
+	wget -U MoinMoin/Makefile -O build/INSTALL.html "http://master18.moinmo.in/MoinMoin/InstallDocs?action=print"
 	sed \
-		-e 's#href="/#href="http://master17.moinmo.in/#g' \
+		-e 's#href="/#href="http://master18.moinmo.in/#g' \
 		-e 's#http://[a-z\.]*/wiki/classic/#/wiki/classic/#g' \
 		-e 's#http://[a-z\.]*/wiki/modern/#/wiki/modern/#g' \
 		-e 's#http://[a-z\.]*/wiki/rightsidebar/#/wiki/rightsidebar/#g' \
@@ -25,9 +25,9 @@
         build/INSTALL.html >docs/INSTALL.html
 	-rm build/INSTALL.html
 
-	wget -U MoinMoin/Makefile -O build/UPDATE.html "http://master17.moinmo.in/HelpOnUpdating?action=print"
+	wget -U MoinMoin/Makefile -O build/UPDATE.html "http://master18.moinmo.in/HelpOnUpdating?action=print"
 	sed \
-		-e 's#href="/#href="http://master17.moinmo.in/#g' \
+		-e 's#href="/#href="http://master18.moinmo.in/#g' \
 		-e 's#http://[a-z\.]*/wiki/classic/#/wiki/classic/#g' \
 		-e 's#http://[a-z\.]*/wiki/modern/#/wiki/modern/#g' \
 		-e 's#http://[a-z\.]*/wiki/rightsidebar/#/wiki/rightsidebar/#g' \
@@ -39,7 +39,7 @@
 	-rmdir build
 
 interwiki:
-	wget -U MoinMoin/Makefile -O $(share)/data/intermap.txt "http://master17.moinmo.in/InterWikiMap?action=raw"
+	wget -U MoinMoin/Makefile -O $(share)/data/intermap.txt "http://master18.moinmo.in/InterWikiMap?action=raw"
 	chmod 664 $(share)/data/intermap.txt
 
 check-tabs:
@@ -47,21 +47,21 @@
 
 # Create documentation
 epydoc: patchlevel
-	@epydoc -o ../html-1.7 --name=MoinMoin --url=http://moinmo.in/ --graph=all --graph-font=Arial MoinMoin
+	@epydoc -o ../html-1.8 --name=MoinMoin --url=http://moinmo.in/ --graph=all --graph-font=Arial MoinMoin
 
 # Create new underlay directory from MoinMaster
 # Should be used only on TW machine
 underlay:
 	rm -rf $(share)/underlay
-	MoinMoin/script/moin.py --config-dir=/srv/moin/cfg/1.7 --wiki-url=master17.moinmo.in/ maint globaledit
-	MoinMoin/script/moin.py --config-dir=/srv/moin/cfg/1.7 --wiki-url=master17.moinmo.in/ maint reducewiki --target-dir=$(share)/underlay
+	MoinMoin/script/moin.py --config-dir=/srv/moin/cfg/1.8 --wiki-url=master18.moinmo.in/ maint globaledit
+	MoinMoin/script/moin.py --config-dir=/srv/moin/cfg/1.8 --wiki-url=master18.moinmo.in/ maint reducewiki --target-dir=$(share)/underlay
 	rm -rf $(share)/underlay/pages/InterWikiMap
 	rm -rf $(share)/underlay/pages/MoinPagesEditorGroup
 	cd $(share); rm -f underlay.tar; tar cf underlay.tar underlay
 
 pagepacks:
 	@python MoinMoin/_tests/maketestwiki.py
-	@MoinMoin/script/moin.py --config-dir=$(testwiki)/.. maint mkpagepacks
+	@MoinMoin/script/moin.py --config-dir=MoinMoin/_tests maint mkpagepacks
 	cd $(share) ; rm -rf underlay
 	cp -a $(testwiki)/underlay $(share)/
 	
--- a/MoinMoin/Page.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/Page.py	Mon Apr 06 12:28:51 2009 +0200
@@ -554,7 +554,7 @@
             if editordata[0] == 'interwiki':
                 editor = "%s:%s" % editordata[1]
             else:
-                editor = editordata[1] # ip or email
+                editor = editordata[1] # ip or email or anon
             result = {
                 'timestamp': line.ed_time_usecs,
                 'editor': editor,
@@ -755,7 +755,10 @@
 
         # Add anchor
         if anchor:
-            url = "%s#%s" % (url, wikiutil.url_quote_plus(anchor))
+            fmt = getattr(self, 'formatter', request.html_formatter)
+            if fmt:
+                anchor = fmt.sanitize_to_id(anchor)
+            url = "%s#%s" % (url, anchor)
 
         if not relative:
             url = '%s/%s' % (request.getScriptname(), url)
@@ -1026,10 +1029,11 @@
             # redirect to another page
             # note that by including "action=show", we prevent endless looping
             # (see code in "request") or any cascaded redirection
-            request.http_redirect('%s/%s?action=show&redirect=%s' % (
-                request.getScriptname(),
-                wikiutil.quoteWikinameURL(pi['redirect']),
-                wikiutil.url_quote_plus(self.page_name, ''), ))
+            pagename, anchor = wikiutil.split_anchor(pi['redirect'])
+            redirect_url = Page(request, pagename).url(request,
+                                                       querystr={'action': 'show', 'redirect': self.page_name, },
+                                                       anchor=anchor)
+            request.http_redirect(redirect_url)
             return
 
         # if necessary, load the formatter
--- a/MoinMoin/PageEditor.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/PageEditor.py	Mon Apr 06 12:28:51 2009 +0200
@@ -24,6 +24,7 @@
 from MoinMoin.widget import html
 from MoinMoin.widget.dialog import Status
 from MoinMoin.logfile import editlog, eventlog
+from MoinMoin.mail.sendmail import encodeSpamSafeEmail
 from MoinMoin.support.python_compatibility import set
 from MoinMoin.util import filesys, timefuncs, web
 from MoinMoin.events import PageDeletedEvent, PageRenamedEvent, PageCopiedEvent, PageRevertedEvent
@@ -483,7 +484,8 @@
 
         # QuickHelp originally by Georg Mischler <schorsch@lightingwiki.com>
         markup = self.pi['format'] or request.cfg.default_markup
-        quickhelp = request.cfg.editor_quickhelp.get(markup, "")
+        parser = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", markup)
+        quickhelp = getattr(parser, 'quickhelp', None)
         if quickhelp:
             request.write(request.formatter.div(1, id="editor-help"))
             request.write(_(quickhelp, wiki=True))
@@ -763,6 +765,7 @@
         request = self.request
         now = self._get_local_timestamp()
         u = request.user
+        obfuscated_email_address = encodeSpamSafeEmail(u.email)
         signature = u.signature()
         variables = {
             'PAGE': self.page_name,
@@ -772,6 +775,7 @@
             'USERNAME': signature,
             'USER': "-- %s" % signature,
             'SIG': "-- %s <<DateTime(%s)>>" % (signature, now),
+            'EMAIL': "<<MailTo(%s)>>" % (obfuscated_email_address)
         }
 
         if u.valid and u.name:
--- a/MoinMoin/PageGraphicalEditor.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/PageGraphicalEditor.py	Mon Apr 06 12:28:51 2009 +0200
@@ -314,7 +314,7 @@
         url_prefix_static = request.cfg.url_prefix_static
         url_prefix_local = request.cfg.url_prefix_local
         wikipage = wikiutil.quoteWikinameURL(self.page_name)
-        fckbasepath = url_prefix_local + '/applets/FCKeditor'
+        fckbasepath = request.cfg.url_prefix_fckeditor
         wikiurl = request.getScriptname()
         if not wikiurl or wikiurl[-1] != '/':
             wikiurl += '/'
--- a/MoinMoin/__init__.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/__init__.py	Mon Apr 06 12:28:51 2009 +0200
@@ -1,9 +1,9 @@
 # -*- coding: iso-8859-1 -*-
 """
-MoinMoin Version 1.7.3
+MoinMoin - a wiki engine in Python
 
 @copyright: 2000-2006 by Juergen Hermann <jh@web.de>,
-            2002-2008 MoinMoin:ThomasWaldmann
+            2002-2009 MoinMoin:ThomasWaldmann
 @license: GNU GPL, see COPYING for details.
 """
 
--- a/MoinMoin/_tests/_test_template.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/_tests/_test_template.py	Mon Apr 06 12:28:51 2009 +0200
@@ -45,19 +45,17 @@
         ('Line break',  '<<BR>>',        '<br>'),
     )
 
+    from MoinMoin._tests import wikiconfig
+    class Config(wikiconfig.Config):
+        foo = 'bar'  # we want to have this non-default setting
+
     def setup_class(self):
         """ Stuff that should be run to init the state of this test class
-
-        Some test needs specific config values, or they will fail.
         """
-        self.config = self.TestConfig(defaults=['this option', 'that option'],
-                                      another_option='non default value')
 
     def teardown_class(self):
         """ Stuff that should run to clean up the state of this test class
-
         """
-        self.config.reset()
 
     def testFunction(self):
         """ module_tested: function should... """
--- a/MoinMoin/_tests/compat.py	Mon Apr 06 12:27:11 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-""" UnitTest compatiblity code, from the py lib. MIT licensed originally, copyright Holger Krekel. """
-
-import py
-from py.__.test.outcome import Failed, Passed
-
-
-class TestCaseUnit(py.test.collect.Function):
-    """ compatibility Unit executor for TestCase methods
-        honouring setUp and tearDown semantics.
-    """
-    def execute(self, session):
-        boundmethod = self.obj
-        instance = boundmethod.im_self
-        instance.setUp()
-        try:
-            boundmethod()
-        finally:
-            instance.tearDown()
-        return Passed()
-
-class TestCase(object):
-    """compatibility class of unittest's TestCase. """
-    Function = TestCaseUnit
-
-    def setUp(self):
-        pass
-
-    def tearDown(self):
-        pass
-
-    def fail(self, msg=None):
-        """ fail immediate with given message. """
-        raise Failed(msg=msg)
-
-    def assertRaises(self, excclass, func, *args, **kwargs):
-        py.test.raises(excclass, func, *args, **kwargs)
-    failUnlessRaises = assertRaises
-
-    # dynamically construct (redundant) methods
-    aliasmap = [
-        ('x',   'not x', 'assert_, failUnless'),
-        ('x',   'x',     'failIf'),
-        ('x,y', 'x!=y',  'failUnlessEqual,assertEqual, assertEquals'),
-        ('x,y', 'x==y',  'failIfEqual,assertNotEqual, assertNotEquals'),
-        ]
-    items = []
-    for sig, expr, names in aliasmap:
-        names = map(str.strip, names.split(','))
-        sigsubst = expr.replace('y', '%s').replace('x', '%s')
-        for name in names:
-            items.append("""
-                def %(name)s(self, %(sig)s, msg=""):
-                    __tracebackhide__ = True
-                    if %(expr)s:
-                        raise Failed(msg=msg + (%(sigsubst)r %% (%(sig)s)))
-            """ % locals() )
-
-    source = "".join(items)
-    exec py.code.Source(source).compile()
-
-__all__ = ['TestCase']
--- a/MoinMoin/_tests/test_PageEditor.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/_tests/test_PageEditor.py	Mon Apr 06 12:28:51 2009 +0200
@@ -73,11 +73,7 @@
 
     def testExtendedNamesEnabled(self):
         """ PageEditor: expand @USERNAME@ extended name - enabled """
-        try:
-            config = self.TestConfig()
-            assert self.expand() == u'[[%s]]' % self.name
-        finally:
-            del config
+        assert self.expand() == u'[[%s]]' % self.name
 
 
 class TestExpandMailto(TestExpandUserName):
@@ -210,6 +206,7 @@
         """
         simple test if it is possible to delete a Dict page after creation
         """
+        become_trusted(self.request)
         pagename = u'SomeDict'
         page = PageEditor(self.request, pagename, do_editor_backup=0)
         body = u"This is an example text"
--- a/MoinMoin/_tests/test_sourcecode.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/_tests/test_sourcecode.py	Mon Apr 06 12:28:51 2009 +0200
@@ -32,6 +32,8 @@
 
 try:
     import xattr
+    if not hasattr(xattr, "xattr"): # there seem to be multiple modules with that name
+        raise ImportError
     def mark_file_ok(path, mtime):
         x = xattr.xattr(path)
         try:
--- a/MoinMoin/_tests/test_user.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/_tests/test_user.py	Mon Apr 06 12:28:51 2009 +0200
@@ -3,6 +3,7 @@
     MoinMoin - MoinMoin.user Tests
 
     @copyright: 2003-2004 by Juergen Hermann <jh@web.de>
+                2009 by ReimarBauer
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -19,17 +20,17 @@
     def testAscii(self):
         """user: encode ascii password"""
         # u'MoinMoin' and 'MoinMoin' should be encoded to same result
-        expected = "{SHA}X+lk6KR7JuJEH43YnmettCwICdU="
+        expected = "{SSHA}xkDIIx1I7A4gC98Vt/+UelIkTDYxMjM0NQ=="
 
-        result = user.encodePassword("MoinMoin")
+        result = user.encodePassword("MoinMoin", salt='12345')
         assert result == expected
-        result = user.encodePassword(u"MoinMoin")
+        result = user.encodePassword(u"MoinMoin", salt='12345')
         assert result == expected
 
     def testUnicode(self):
         """ user: encode unicode password """
-        result = user.encodePassword(u'סיסמה סודית בהחלט') # Hebrew
-        expected = "{SHA}GvvkgYzv5MoF9Ljivv2oc81FmkE="
+        result = user.encodePassword(u'סיסמה סודית בהחלט', salt='12345') # Hebrew
+        expected = "{SSHA}YiwfeVWdVW9luqyVn8t2JivlzmUxMjM0NQ=="
         assert result == expected
 
 
@@ -99,49 +100,12 @@
         theUser = user.User(self.request, name=name, password=password)
         assert theUser.valid
 
-    def testOldNonAsciiPassword(self):
-        """ user: login with non-ascii password in pre 1.3 user file
-
-        When trying to login with an old non-ascii password in the user
-        file, utf-8 encoded password will not match. In this case, try
-        all other encoding available on pre 1.3 before failing.
-        """
-        # Create test user
-        # Use iso charset to create user with old enc_password, as if
-        # the user file was migrated from pre 1.3 wiki.
-        name = u'__Jürgen Herman__'
-        password = name
-        self.createUser(name, password, charset='iso-8859-1')
-
-        # Try to "login"
-        theUser = user.User(self.request, name=name, password=password)
-        assert theUser.valid
-
-    def testReplaceOldNonAsciiPassword(self):
-        """ user: login replace old non-ascii password in pre 1.3 user file
-
-        When trying to login with an old non-ascii password in the user
-        file, the password hash should be replaced with new utf-8 hash.
-        """
-        # Create test user
-        # Use iso charset to create user with old enc_password, as if
-        # the user file was migrated from pre 1.3 wiki.
-        name = u'__Jürgen Herman__'
-        password = name
-        self.createUser(name, password, charset='iso-8859-1')
-        # Login - this should replace the old password in the user file
-        theUser = user.User(self.request, name=name, password=password)
-        # Login again - the password should be new unicode password
-        expected = user.encodePassword(password)
-        theUser = user.User(self.request, name=name, password=password)
-        assert theUser.enc_password == expected
-
     def testSubscriptionSubscribedPage(self):
         """ user: tests isSubscribedTo  """
         pagename = u'HelpMiscellaneous'
         name = u'__Jürgen Herman__'
         password = name
-        self.createUser(name, password, charset='iso-8859-1')
+        self.createUser(name, password)
         # Login - this should replace the old password in the user file
         theUser = user.User(self.request, name=name, password=password)
         theUser.subscribe(pagename)
@@ -153,7 +117,7 @@
         testPagename = u'HelpMiscellaneous/FrequentlyAskedQuestions'
         name = u'__Jürgen Herman__'
         password = name
-        self.createUser(name, password, charset='iso-8859-1')
+        self.createUser(name, password)
         # Login - this should replace the old password in the user file
         theUser = user.User(self.request, name=name, password=password)
         theUser.subscribe(pagename)
@@ -164,8 +128,6 @@
         if the old username is removed from the cache name2id
         """
         # Create test user
-        # Use iso charset to create user with old enc_password, as if
-        # the user file was migrated from pre 1.3 wiki.
         name = u'__Some Name__'
         password = name
         self.createUser(name, password)
@@ -178,20 +140,52 @@
 
         assert not theUser.exists()
 
+    def test_upgrade_password_to_salted(self):
+        """
+        Create user with {SHA} password and check that logging in
+        upgrades to {SSHA}.
+        """
+        name = u'/no such user/'
+        password = '{SHA}jLIjfQZ5yojbZGTqxg2pY0VROWQ=' # 12345
+        self.createUser(name, password, True)
+        theuser = user.User(self.request, name=name, password='12345')
+        assert theuser.enc_password[:6] == '{SSHA}'
+
+    def test_for_email_attribute_by_name(self):
+        """
+        checks for no access to the email attribute by getting the user object from name
+        """
+        name = u"__TestUser__"
+        password = u"ekfdweurwerh"
+        email = "__TestUser__@moinhost"
+        self.createUser(name, password, email=email)
+        theuser = user.User(self.request, name=name)
+        assert theuser.email == ""
+
+    def test_for_email_attribut_by_uid(self):
+        """
+        checks access to the email attribute by getting the user object from the uid
+        """
+        name = u"__TestUser2__"
+        password = u"ekERErwerwerh"
+        email = "__TestUser2__@moinhost"
+        self.createUser(name, password, email=email)
+        uid = user.getUserId(self.request, name)
+        theuser = user.User(self.request, uid)
+        assert theuser.email == email
+
     # Helpers ---------------------------------------------------------
 
-    def createUser(self, name, password, charset='utf-8'):
+    def createUser(self, name, password, pwencoded=False, email=None):
         """ helper to create test user
-
-        charset is used to create user with pre 1.3 password hash
         """
-        # Hack self.request form to contain the password
-        self.request.form['password'] = [password]
-
         # Create user
         self.user = user.User(self.request)
         self.user.name = name
-        self.user.enc_password = user.encodePassword(password, charset=charset)
+        self.user.email = email
+        if not pwencoded:
+            password = user.encodePassword(password)
+        self.user.enc_password = password
 
         # Validate that we are not modifying existing user data file!
         if self.user.exists():
@@ -209,19 +203,9 @@
 
 class TestGroupName(object):
 
-    def setUp(self):
-        self.config = self.TestConfig(page_group_regex=r'.+Group')
-
-    def tearDown(self):
-        del self.config
-
-    import re
-    group = re.compile(r'.+Group', re.UNICODE)
-
     def testGroupNames(self):
         """ user: isValidName: reject group names """
         test = u'AdminGroup'
-        assert self.group.search(test)
         assert not user.isValidName(self.request, test)
 
 
--- a/MoinMoin/_tests/test_wikiutil.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/_tests/test_wikiutil.py	Mon Apr 06 12:28:51 2009 +0200
@@ -909,9 +909,13 @@
         tests = [
             # text                    expected output
             (u'\xf6\xf6ll\xdf\xdf',   'A.2BAPYA9g-ll.2BAN8A3w-'),
-            (u'level 2',              'level2'),
+            (u'level 2',              'level_2'),
+            (u'level_2',              'level_2'),
             (u'',                     'A'),
             (u'123',                  'A123'),
+            # make sure that a valid anchor is not modified:
+            (u'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_.-',
+             u'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_.-')
         ]
         for text, expected in tests:
             yield self._check, text, expected
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/_tests/wikiconfig.py	Mon Apr 06 12:28:51 2009 +0200
@@ -0,0 +1,33 @@
+# -*- coding: iso-8859-1 -*-
+"""
+MoinMoin - test wiki configuration
+
+Do not change any values without good reason.
+
+We mostly want to have default values here, except for stuff that doesn't
+work without setting them (like data_dir and underlay_dir).
+
+@copyright: 2000-2004 by Juergen Hermann <jh@web.de>
+@license: GNU GPL, see COPYING for details.
+"""
+
+import os
+
+from MoinMoin.config.multiconfig import DefaultConfig
+
+
+class Config(DefaultConfig):
+    sitename = u'Developer Test Wiki'
+    logo_string = sitename
+
+    _base_dir = os.path.join(os.path.dirname(__file__), '../../tests/wiki')
+    data_dir = os.path.join(_base_dir, "data")
+    data_underlay_dir = os.path.join(_base_dir, "underlay")
+
+    #show_hosts = 1
+
+    #secrets = 'some not secret string just to make tests happy'
+
+    # used to check if it is really a wiki we may modify
+    is_test_wiki = True
+
--- a/MoinMoin/action/AttachFile.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/AttachFile.py	Mon Apr 06 12:28:51 2009 +0200
@@ -1038,7 +1038,7 @@
                 fmt.url(0))
         request.write('For using an external program follow this link %s' % link)
         return
-    request.write(m.execute('EmbedObject', u'target=%s, pagename=%s' % (filename, pagename)))
+    request.write(m.execute('EmbedObject', u'target="%s", pagename="%s"' % (filename, pagename)))
     return
 
 
@@ -1107,7 +1107,7 @@
 
         browser = DataBrowserWidget(request)
         browser.setData(data)
-        return browser.toHTML()
+        return browser.render(method="GET")
 
     return ''
 
--- a/MoinMoin/action/Despam.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/Despam.py	Mon Apr 06 12:28:51 2009 +0200
@@ -71,7 +71,7 @@
 
     table = DataBrowserWidget(request)
     table.setData(dataset)
-    table.render()
+    return table.render(method="GET")
 
 class tmp:
     pass
@@ -197,7 +197,7 @@
     elif editor:
         show_pages(request, pagename, editor, timestamp)
     else:
-        show_editors(request, pagename, timestamp)
+        request.write(show_editors(request, pagename, timestamp))
 
     # End content and send footer
     request.write(request.formatter.endContent())
--- a/MoinMoin/action/SyncPages.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/SyncPages.py	Mon Apr 06 12:28:51 2009 +0200
@@ -46,8 +46,12 @@
         self.status = []
         self.rollback = set()
 
-    def log_status(self, level, message="", substitutions=(), raw_suffix=""):
+    def log_status(self, level, message=u"", substitutions=(), raw_suffix=u""):
         """ Appends the message with a given importance level to the internal log. """
+        if isinstance(message, str):
+            message = message.decode("utf-8")
+        if isinstance(raw_suffix, str):
+            raw_suffix = raw_suffix.decode("utf-8")
         self.status.append((level, message, substitutions, raw_suffix))
 
     def register_rollback(self, func):
@@ -74,17 +78,18 @@
         table = []
 
         for line in self.status:
-            if line[1]:
-                if line[2]:
-                    macro_args = [line[1]] + list(line[2])
+            level, message, substitutions, raw_suffix = line
+            if message:
+                if substitutions:
+                    macro_args = [message] + list(substitutions)
                     message = u"<<GetText2(|%s)>>" % (packLine(macro_args), )
                 else:
-                    message = u"<<GetText(%s)>>" % (line[1], )
+                    message = u"<<GetText(%s)>>" % (message, )
             else:
                 message = u""
-            table.append(table_line % {"smiley": line[0][1],
+            table.append(table_line % {"smiley": level[1],
                                        "message": message,
-                                       "raw_suffix": line[3].replace("\n", "<<BR>>")})
+                                       "raw_suffix": raw_suffix.replace("\n", "<<BR>>")})
 
         return "\n".join(table)
 
--- a/MoinMoin/action/backup.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/backup.py	Mon Apr 06 12:28:51 2009 +0200
@@ -1,12 +1,18 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - make or restore a full backup of the wiki
+    MoinMoin - download a backup via http.
 
-    Triggering backup action will check if you are authorized to do
-    a backup and if yes, just send a
-    <siteid>-<date>--<time>.tar.<format> to you.
+    Triggering backup action will check if you are authorized to do a backup
+    and if yes, just send a <siteid>-<date>--<time>.tar.<format> to you.
+    What exactly is contained in your backup depends on your wiki's
+    configuration - please make sure you have everything you need BEFORE you
+    really need it.
 
-    @copyright: 2005 by MoinMoin:ThomasWaldmann
+    Note: there is no restore support, you need somebody having access to your
+          wiki installation via the server's file system, knowing about tar
+          and restoring your data CAREFULLY (AKA "the server admin").
+
+    @copyright: 2005-2008 by MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -15,81 +21,51 @@
 from MoinMoin import wikiutil
 from MoinMoin.support import tarfile
 
-def addFiles(path, tar, exclude):
+
+def addFiles(path, tar, exclude_func):
     """ Add files in path to tar """
     for root, dirs, files in os.walk(path):
         files.sort() # sorted page revs may compress better
         for name in files:
             path = os.path.join(root, name)
-            if exclude.search(path):
+            if exclude_func(path):
                 continue
             tar.add(path)
 
+
 def sendBackup(request):
     """ Send compressed tar file """
     dateStamp = time.strftime("%Y-%m-%d--%H-%M-%S-UTC", time.gmtime())
     filename = "%s-%s.tar.%s" % (request.cfg.siteid, dateStamp, request.cfg.backup_compression)
     request.emit_http_headers([
-        "Content-Type: application/octet-stream",
-        "Content-Disposition: inline; filename=\"%s\"" % filename, ])
+        'Content-Type: application/octet-stream',
+        'Content-Disposition: inline; filename="%s"' % filename, ])
 
     tar = tarfile.open(fileobj=request, mode="w|%s" % request.cfg.backup_compression)
     # allow GNU tar's longer file/pathnames
     tar.posix = False
-    exclude = re.compile("|".join(request.cfg.backup_exclude))
     for path in request.cfg.backup_include:
-        addFiles(path, tar, exclude)
+        addFiles(path, tar, request.cfg.backup_exclude)
     tar.close()
 
-def restoreBackup(request, pagename):
-    _ = request.getText
-    path = request.cfg.backup_storage_dir
-    filename = "%s.tar.%s" % (request.cfg.siteid, request.cfg.backup_compression)
-    filename = os.path.join(path, filename)
-    targetdir = request.cfg.backup_restore_target_dir
-    try:
-        tar = tarfile.open(fileobj=file(filename), mode="r|%s" % request.cfg.backup_compression)
-        # allow GNU tar's longer file/pathnames
-        tar.posix = False
-        files = []
-        dirs = []
-        for m in tar:
-            if m.isdir():
-                dirs.append("%s %s %s" % (m.name, m.size, m.mtime))
-            else:
-                files.append("%s %s %s" % (m.name, m.size, m.mtime))
-            tar.extract(m, targetdir)
-        tar.close()
-        #files = "<br>".join(files)
-        filecount = len(files)
-        dircount = len(dirs)
-        return sendMsg(request, pagename,
-            msg=_('Restored Backup: %(filename)s to target dir: %(targetdir)s.\nFiles: %(filecount)d, Directories: %(dircount)d') %
-                locals(), msgtype="info")
-    except:
-        return sendMsg(request, pagename, msg=_("Restoring backup: %(filename)s to target dir: %(targetdir)s failed.") % locals(), msgtype="info")
 
 def sendBackupForm(request, pagename):
     _ = request.getText
     request.emit_http_headers()
     request.setContentLanguage(request.lang)
-    title = _('Wiki Backup / Restore')
+    title = _('Wiki Backup')
     request.theme.send_title(title, form=request.form, pagename=pagename)
     request.write(request.formatter.startContent("content"))
 
-    request.write(_("""Some hints:
- * To restore a backup:
-  * Restoring a backup will overwrite existing data, so be careful.
-  * Rename it to <siteid>.tar.<compression> (remove the --date--time--UTC stuff).
-  * Put the backup file into the backup_storage_dir (use scp, ftp, ...).
-  * Hit the <<GetText(Restore)>> button below.
+    request.write(_("""= Downloading a backup =
 
- * To make a backup, just hit the <<GetText(Backup)>> button and save the file
-   you get to a secure place.
+Please note:
+ * Store backups in a safe and secure place - they contain sensitive information.
+ * Make sure your wiki configuration backup_* values are correct and complete.
+ * Make sure the backup file you get contains everything you need in case of problems.
+ * Make sure it is downloaded without problems.
 
-Please make sure your wiki configuration backup_* values are correct and complete.
-
-""", wiki=True))
+To get a backup, just click here:""", wiki=True))
 
     request.write("""
 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
@@ -97,34 +73,30 @@
 <input type="hidden" name="do" value="backup">
 <input type="submit" value="%(backup_button)s">
 </form>
-
-<form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
-<input type="hidden" name="action" value="backup">
-<input type="hidden" name="do" value="restore">
-<input type="submit" value="%(restore_button)s">
-</form>
 """ % {
     'baseurl': request.getScriptname(),
     'pagename': wikiutil.quoteWikinameURL(pagename),
     'backup_button': _('Backup'),
-    'restore_button': _('Restore'),
 })
 
     request.write(request.formatter.endContent())
     request.theme.send_footer(pagename)
     request.theme.send_closing_html()
 
+
 def sendMsg(request, pagename, msg, msgtype):
     from MoinMoin import Page
     request.theme.add_msg(msg, msgtype)
     return Page.Page(request, pagename).send_page()
 
+
 def backupAllowed(request):
     """ Return True if backup is allowed """
     action = __name__.split('.')[-1]
     user = request.user
     return user.valid and user.name in request.cfg.backup_users
 
+
 def execute(pagename, request):
     _ = request.getText
     if not backupAllowed(request):
@@ -134,10 +106,9 @@
     dowhat = request.form.get('do', [None])[0]
     if dowhat == 'backup':
         sendBackup(request)
-    elif dowhat == 'restore':
-        restoreBackup(request, pagename)
     elif dowhat is None:
         sendBackupForm(request, pagename)
     else:
         return sendMsg(request, pagename,
                        msg=_('Unknown backup subaction: %s.') % dowhat, msgtype="error")
+
--- a/MoinMoin/action/cache.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/cache.py	Mon Apr 06 12:28:51 2009 +0200
@@ -27,8 +27,6 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-import hmac, sha
-
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
@@ -39,6 +37,7 @@
 from MoinMoin import config, caching
 from MoinMoin.util import filesys
 from MoinMoin.action import AttachFile
+from MoinMoin.support.python_compatibility import hmac_new
 
 action_name = __name__.split('.')[-1]
 
@@ -87,7 +86,7 @@
     @param secret: secret for hMAC calculation (default: use secret from cfg)
     """
     if secret is None:
-        secret = request.cfg.secrets
+        secret = request.cfg.secrets['action/cache']
     if content:
         hmac_data = content
     elif itemname is not None and attachname is not None:
@@ -98,7 +97,7 @@
         raise AssertionError('cache_key called with unsupported parameters')
 
     hmac_data = hmac_data.encode('utf-8')
-    key = hmac.new(secret, hmac_data, sha).hexdigest()
+    key = hmac_new(secret, hmac_data).hexdigest()
     return key
 
 
--- a/MoinMoin/action/fullsearch.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/fullsearch.py	Mon Apr 06 12:28:51 2009 +0200
@@ -179,21 +179,22 @@
         sort = 'weight'
 
     # search the pages
-    from MoinMoin.search import searchPages, QueryParser
+    from MoinMoin.search import searchPages, QueryParser, QueryError
     try:
         query = QueryParser(case=case, regex=regex,
                 titlesearch=titlesearch).parse_query(needle)
-        results = searchPages(request, query, sort, mtime, historysearch)
-    except ValueError: # catch errors in the search query
+    except QueryError: # catch errors in the search query
         request.theme.add_msg(_('Your search query {{{"%s"}}} is invalid. Please refer to '
                 'HelpOnSearching for more information.', wiki=True, percent=True) % wikiutil.escape(needle), "error")
         Page(request, pagename).send_page()
         return
 
-    # directly show a single hit
-    # Note: can't work with attachment search
-    # improve if we have one...
-    if len(results.hits) == 1:
+    results = searchPages(request, query, sort, mtime, historysearch)
+
+    # directly show a single hit for title searches
+    # this is the "quick jump" functionality if you don't remember
+    # the pagename exactly, but just some parts of it
+    if titlesearch and len(results.hits) == 1:
         page = results.hits[0]
         if not page.attachment: # we did not find an attachment
             page = Page(request, page.page_name)
@@ -205,7 +206,7 @@
             url = page.url(request, querystr=querydict)
             request.http_redirect(url)
             return
-    elif not results.hits: # no hits?
+    if not results.hits: # no hits?
         f = request.formatter
         querydict = wikiutil.parseQueryString(request.query_string)
         querydict.update({'titlesearch': 0})
--- a/MoinMoin/action/info.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/info.py	Mon Apr 06 12:28:51 2009 +0200
@@ -32,8 +32,8 @@
                       f.text(_("Page size: %d") % page.size()),
                       f.paragraph(0))
 
-        import sha
-        digest = sha.new(page.get_raw_body().encode(config.charset)).hexdigest().upper()
+        from MoinMoin.support.python_compatibility import hash_new
+        digest = hash_new('sha1', page.get_raw_body().encode(config.charset)).hexdigest().upper()
         request.write(f.paragraph(1),
                       f.rawHTML('%(label)s <tt>%(value)s</tt>' % {
                           'label': _("SHA digest of this page's content is:"),
@@ -176,7 +176,7 @@
 
         div = html.DIV(id="page-history")
         div.append(html.INPUT(type="hidden", name="action", value="diff"))
-        div.append(history_table.toHTML())
+        div.append(history_table.render(method="GET"))
 
         form = html.FORM(method="GET", action="")
         form.append(div)
@@ -218,7 +218,7 @@
 
     if show_hitcounts:
         from MoinMoin.stats import hitcounts
-        request.write(hitcounts.linkto(pagename, request, 'page=' + wikiutil.url_quote_plus(pagename)))
+        request.write(hitcounts.linkto(pagename, request, 'page=' + wikiutil.url_quote(pagename)))
     elif show_general:
         general(page, pagename, request)
     else:
--- a/MoinMoin/action/login.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/login.py	Mon Apr 06 12:28:51 2009 +0200
@@ -64,13 +64,9 @@
         if islogin: # user pressed login button
             if request._login_multistage:
                 return self.handle_multistage()
-            error = []
             if hasattr(request, '_login_messages'):
                 for msg in request._login_messages:
-                    error.append('<p>')
-                    error.append(msg)
-                error = ''.join(error)
-            request.theme.add_msg(error, "error")
+                    request.theme.add_msg(msg, "error")
             return self.page.send_page()
 
         else: # show login form
--- a/MoinMoin/action/newaccount.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/newaccount.py	Mon Apr 06 12:28:51 2009 +0200
@@ -56,7 +56,7 @@
 
     pw_checker = request.cfg.password_checker
     if pw_checker:
-        pw_error = pw_checker(theuser.name, password)
+        pw_error = pw_checker(request, theuser.name, password)
         if pw_error:
             return _("Password not acceptable: %s") % pw_error
 
@@ -83,9 +83,6 @@
     # save data
     theuser.save()
 
-    if form.has_key('create_and_mail'):
-        theuser.mailAccountData()
-
     result = _("User account created! You can use this account to login now...")
     if _debug:
         result = result + util.dumpFormData(form)
@@ -148,13 +145,8 @@
     row.append(html.TD())
     td = html.TD()
     row.append(td)
-    td.append(html.INPUT(type="submit", name="create_only",
+    td.append(html.INPUT(type="submit", name="create",
                          value=_('Create Profile')))
-    if request.cfg.mail_enabled:
-        td.append(html.Text(' '))
-        td.append(html.INPUT(type="submit", name="create_and_mail",
-                             value="%s + %s" % (_('Create Profile'),
-                                                _('Email'))))
 
     return unicode(ret)
 
@@ -174,7 +166,7 @@
     _ = request.getText
     form = request.form
 
-    submitted = form.has_key('create_only') or form.has_key('create_and_mail')
+    submitted = form.has_key('create')
 
     if submitted: # user pressed create button
         request.theme.add_msg(_create_user(request), "dialog")
--- a/MoinMoin/action/quickunlink.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/quickunlink.py	Mon Apr 06 12:28:51 2009 +0200
@@ -22,5 +22,7 @@
             msg = _('Your quicklink to this page could not be removed.')
     else:
         msg = _('You need to have a quicklink to this page to remove it.')
+    if msg:
+        request.theme.add_msg(msg)
+    Page(request, pagename).send_page()
 
-    Page(request, pagename).send_page(msg=msg)
--- a/MoinMoin/action/recoverpass.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/recoverpass.py	Mon Apr 06 12:28:51 2009 +0200
@@ -173,7 +173,7 @@
             pw_checker = request.cfg.password_checker
             pw_error = None
             if pw_checker:
-                pw_error = pw_checker(name, newpass)
+                pw_error = pw_checker(request, name, newpass)
                 if pw_error:
                     msg = _("Password not acceptable: %s") % pw_error
             if not pw_error:
--- a/MoinMoin/action/unsubscribe.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/action/unsubscribe.py	Mon Apr 06 12:28:51 2009 +0200
@@ -16,18 +16,16 @@
         request.theme.add_msg(_("You must login to use this action: %(action)s.") % {"action": actname}, "error")
         return Page(request, pagename).send_page()
 
-    msg = None
-
     if request.user.isSubscribedTo([pagename]):
         # Try to unsubscribe
         if request.user.unsubscribe(pagename):
-            msg = _('Your subscription to this page has been removed.')
+            request.theme.add_msg(_('Your subscription to this page has been removed.'), "info")
         else:
             msg = _("Can't remove regular expression subscription!") + u' ' + \
                   _("Edit the subscription regular expressions in your settings.")
+            request.theme.add_msg(msg, "error")
     else:
         # The user is not subscribed
-        msg = _('You need to be subscribed to unsubscribe.')
+        request.theme.add_msg(_('You need to be subscribed to unsubscribe.'), "info")
+    Page(request, pagename).send_page()
 
-    Page(request, pagename).send_page(msg=msg)
-
--- a/MoinMoin/auth/_tests/test_auth.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/_tests/test_auth.py	Mon Apr 06 12:28:51 2009 +0200
@@ -6,13 +6,17 @@
     @license: GNU GPL, see COPYING for details.
 """
 
+import py.test
+py.test.skip("broken due to test Config refactoring, fix later")
+
 import StringIO, urllib
 
 from MoinMoin.server.server_wsgi import WsgiConfig
 from MoinMoin.request import request_wsgi
+from MoinMoin._tests import wikiconfig
 
 
-class TestAuth:
+class AuthTest:
     """ test misc. auth methods """
     PAGES = ['FrontPage', 'MoinMoin', 'HelpContents', 'WikiSandBox', ] # must all exist!
 
@@ -56,6 +60,8 @@
         request.user = save_user
         return request # request.status, request.headers, request.output()
 
+
+class TestNoAuth(AuthTest):
     def testNoAuth(self):
         """ run a simple request, no auth, just check if it succeeds """
         environ = self.setup_env()
@@ -87,9 +93,12 @@
         output = request.output()
         assert '</html>' in output
 
+class TestAnonSession(AuthTest):
+    class Config(wikiconfig.Config):
+        anonymous_session_lifetime = 1
+
     def testAnonSession(self):
         """ run some requests, no auth, check if anon sessions work """
-        self.config = self.TestConfig(anonymous_session_lifetime=1)
         cookie = ''
         trail_expected = []
         first = True
@@ -146,11 +155,14 @@
             trail = request.session['trail']
             assert trail == trail_expected
 
+class TestHttpAuthSession(AuthTest):
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.http import HTTPAuth
+        auth = [HTTPAuth(autocreate=True)]
+
     def testHttpAuthSession(self):
         """ run some requests with http auth, check whether session works """
-        from MoinMoin.auth.http import HTTPAuth
         username = u'HttpAuthTestUser'
-        self.config = self.TestConfig(auth=[HTTPAuth()], user_autocreate=True)
         cookie = ''
         trail_expected = []
         first = True
@@ -206,13 +218,16 @@
             trail = request.session['trail']
             assert trail == trail_expected
 
+class TestMoinAuthSession(AuthTest):
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth import MoinAuth
+        auth = [MoinAuth()]
+
     def testMoinAuthSession(self):
         """ run some requests with MoinAuth, check whether session works """
-        from MoinMoin.auth import MoinAuth
         from MoinMoin.user import User
-        self.config = self.TestConfig(auth=[MoinAuth()])
         username = u'MoinAuthTestUser'
-        password = u'secret'
+        password = u'ßecretß'
         User(self.request, name=username, password=password).save() # create user
         trail_expected = []
         first = True
--- a/MoinMoin/auth/_tests/test_ldap_login.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/_tests/test_ldap_login.py	Mon Apr 06 12:28:51 2009 +0200
@@ -7,10 +7,11 @@
 """
 
 import py.test
+py.test.skip("Broken due to test Config refactoring")
 
 from MoinMoin._tests.ldap_testbase import LDAPTstBase, LdapEnvironment, check_environ, SLAPD_EXECUTABLE
 from MoinMoin._tests.ldap_testdata import *
-from MoinMoin._tests import nuke_user
+from MoinMoin._tests import nuke_user, wikiconfig
 
 # first check if we have python 2.4, python-ldap and slapd:
 msg = check_environ()
@@ -20,7 +21,7 @@
 
 import ldap
 
-class TestSimpleLdap(LDAPTstBase):
+class TestLDAPServer(LDAPTstBase):
     basedn = BASEDN
     rootdn = ROOTDN
     rootpw = ROOTPW
@@ -39,14 +40,23 @@
         assert 'usera' in uids
         assert 'userb' in uids
 
+class TestMoinLDAPLogin(LDAPTstBase):
+    basedn = BASEDN
+    rootdn = ROOTDN
+    rootpw = ROOTPW
+    slapd_config = SLAPD_CONFIG
+    ldif_content = LDIF_CONTENT
+
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.ldap_login import LDAPAuth
+        server_uri = self.ldap_env.slapd.url # XXX no self
+        base_dn = self.ldap_env.basedn
+        ldap_auth1 = LDAPAuth(server_uri=server_uri, base_dn=base_dn, autocreate=True)
+        auth = [ldap_auth1, ]
+
     def testMoinLDAPLogin(self):
         """ Just try accessing the LDAP server and see if usera and userb are in LDAP. """
-        server_uri = self.ldap_env.slapd.url
-        base_dn = self.ldap_env.basedn
 
-        from MoinMoin.auth.ldap_login import LDAPAuth
-        ldap_auth1 = LDAPAuth(server_uri=server_uri, base_dn=base_dn)
-        self.config = self.TestConfig(auth=[ldap_auth1, ], user_autocreate=True)
         handle_auth = self.request.handle_auth
 
         # tests that must not authenticate:
@@ -79,6 +89,15 @@
     slapd_config = SLAPD_CONFIG
     ldif_content = LDIF_CONTENT
 
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.ldap_login import LDAPAuth
+        from MoinMoin.auth import MoinAuth
+        server_uri = self.ldap_env.slapd.url # XXX no self
+        base_dn = self.ldap_env.basedn
+        ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn, autocreate=True)
+        moin_auth = MoinAuth()
+        auth = [ldap_auth, moin_auth]
+
     def teardown_class(self):
         """ Stop slapd, remove LDAP server environment """
         #self.ldap_env.stop_slapd()  # it is already stopped
@@ -89,14 +108,6 @@
             a default password there), then try logging in via moin login using
             that default password or an empty password.
         """
-        server_uri = self.ldap_env.slapd.url
-        base_dn = self.ldap_env.basedn
-
-        from MoinMoin.auth.ldap_login import LDAPAuth
-        ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn)
-        from MoinMoin.auth import MoinAuth
-        moin_auth = MoinAuth()
-        self.config = self.TestConfig(auth=[ldap_auth, moin_auth], user_autocreate=True)
 
         nuke_user(self.request, u'usera')
 
@@ -171,6 +182,18 @@
     slapd_config = SLAPD_CONFIG
     ldif_content = LDIF_CONTENT
 
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.ldap_login import LDAPAuth
+        authlist = []
+        for ldap_env in self.ldap_envs: # XXX no self
+            server_uri = ldap_env.slapd.url
+            base_dn = ldap_env.basedn
+            ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn,
+                                 autocreate=True,
+                                 timeout=1) # short timeout, faster testing
+            authlist.append(ldap_auth)
+        auth = authlist
+
     def setup_class(self):
         """ Create LDAP servers environment, start slapds """
         self.ldap_envs = []
@@ -195,16 +218,6 @@
 
     def testMoinLDAPFailOver(self):
         """ Try if it does a failover to a secondary LDAP, if the primary fails. """
-        from MoinMoin.auth.ldap_login import LDAPAuth
-        authlist = []
-        for ldap_env in self.ldap_envs:
-            server_uri = ldap_env.slapd.url
-            base_dn = ldap_env.basedn
-            ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn,
-                                 timeout=1) # short timeout, faster testing
-            authlist.append(ldap_auth)
-
-        self.config = self.TestConfig(auth=authlist, user_autocreate=True)
         handle_auth = self.request.handle_auth
 
         # authenticate user (with primary slapd):
--- a/MoinMoin/auth/http.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/http.py	Mon Apr 06 12:28:51 2009 +0200
@@ -22,6 +22,10 @@
     """ authenticate via http basic/digest/ntlm auth """
     name = 'http'
 
+    def __init__(self, autocreate=False):
+        self.autocreate = autocreate
+        BaseAuth.__init__(self)
+
     def request(self, request, user_obj, **kw):
         u = None
         _ = request.getText
@@ -72,7 +76,7 @@
                 u = user.User(request, auth_username=username,
                               auth_method=self.name, auth_attribs=('name', 'password'))
 
-        if u:
+        if u and self.autocreate:
             u.create_or_update()
         if u and u.valid:
             return u, True # True to get other methods called, too
--- a/MoinMoin/auth/interwiki.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/interwiki.py	Mon Apr 06 12:28:51 2009 +0200
@@ -20,9 +20,10 @@
     logout_possible = True
     login_inputs = ['username', 'password']
 
-    def __init__(self, trusted_wikis):
+    def __init__(self, trusted_wikis, autocreate=False):
         BaseAuth.__init__(self)
         self.trusted_wikis = trusted_wikis
+        self.autocreate = autocreate
 
     def login(self, request, user_obj, **kw):
         username = kw.get('username')
@@ -68,7 +69,8 @@
             if key not in request.cfg.user_transient_fields:
                 setattr(u, key, value)
         u.valid = True
-        u.create_or_update(True)
+        if self.autocreate:
+            u.create_or_update(True)
         logging.debug("successful interwiki auth for %r" % name)
         return ContinueLogin(u)
 
--- a/MoinMoin/auth/ldap_login.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/ldap_login.py	Mon Apr 06 12:28:51 2009 +0200
@@ -72,6 +72,7 @@
         aliasname_attribute=None, # ('displayName') ldap attribute we get the aliasname from
         email_attribute=None, # ('mail') ldap attribute we get the email address from
         email_callback=None, # called to make up email address
+        name_callback=None, # called to use a Wiki name different from the login name
         coding='utf-8', # coding used for ldap queries and result values
         timeout=10, # how long we wait for the ldap server [s]
         start_tls=0, # 0 = No, 1 = Try, 2 = Required
@@ -81,6 +82,7 @@
         tls_keyfile=None,
         tls_require_cert=0, # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
         bind_once=False, # set to True to only do one bind - useful if configured to bind as the user on the first attempt
+        autocreate=False, # set to True if you want to autocreate user profiles
         ):
         self.server_uri = server_uri
         self.bind_dn = bind_dn
@@ -95,6 +97,7 @@
         self.aliasname_attribute = aliasname_attribute
         self.email_attribute = email_attribute
         self.email_callback = email_callback
+        self.name_callback = name_callback
 
         self.coding = coding
         self.timeout = timeout
@@ -107,7 +110,7 @@
         self.tls_require_cert = tls_require_cert
 
         self.bind_once = bind_once
-
+        self.autocreate = autocreate
 
     def login(self, request, user_obj, **kw):
         username = kw.get('username')
@@ -217,6 +220,9 @@
                         aliasname = sn
                 aliasname = aliasname.decode(coding)
 
+                if self.name_callback:
+                    username = self.name_callback(ldap_dict)
+
                 if email:
                     u = user.User(request, auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'email', 'mailto_author', ))
                     u.email = email
@@ -231,7 +237,7 @@
                 logging.debug("invalid credentials (wrong password?) for dn %r (username: %r)" % (dn, username))
                 return CancelLogin(_("Invalid username or password."))
 
-            if u:
+            if u and self.autocreate:
                 u.create_or_update(True)
             return ContinueLogin(u)
 
--- a/MoinMoin/auth/openidrp.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/openidrp.py	Mon Apr 06 12:28:51 2009 +0200
@@ -25,12 +25,14 @@
     def __init__(self, modify_request=None,
                        update_user=None,
                        create_user=None,
-                       forced_service=None):
+                       forced_service=None,
+                       idselector_com=None):
         BaseAuth.__init__(self)
         self._modify_request = modify_request or (lambda x: None)
         self._update_user = update_user or (lambda i, u: None)
         self._create_user = create_user or (lambda i, u: None)
         self._forced_service = forced_service
+        self._idselector_com = idselector_com
         if forced_service:
             self.login_inputs = ['special_no_input']
 
@@ -294,5 +296,9 @@
 
     def login_hint(self, request):
         _ = request.getText
-        return _("If you do not have an account yet, you can still log in "
+        msg = u''
+        if self._idselector_com:
+            msg = self._idselector_com
+        msg += _("If you do not have an account yet, you can still log in "
                  "with your OpenID and create one during login.")
+        return msg
--- a/MoinMoin/auth/php_session.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/php_session.py	Mon Apr 06 12:28:51 2009 +0200
@@ -21,7 +21,7 @@
 
     name = 'php_session'
 
-    def __init__(self, apps=['egw'], s_path="/tmp", s_prefix="sess_"):
+    def __init__(self, apps=['egw'], s_path="/tmp", s_prefix="sess_", autocreate=False):
         """ @param apps: A list of the enabled applications. See above for
             possible keys.
             @param s_path: The path where the PHP sessions are stored.
@@ -31,6 +31,7 @@
         self.s_path = s_path
         self.s_prefix = s_prefix
         self.apps = apps
+        self.autocreate = autocreate
 
     def request(self, request, user_obj, **kw):
         def handle_egroupware(session):
@@ -72,7 +73,7 @@
                 u.email = email
                 changed = True
 
-            if u:
+            if u and self.autocreate:
                 u.create_or_update(changed)
             if u and u.valid:
                 return u, True # True to get other methods called, too
--- a/MoinMoin/auth/sslclientcert.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/auth/sslclientcert.py	Mon Apr 06 12:28:51 2009 +0200
@@ -21,13 +21,15 @@
 
     def __init__(self, authorities=None,
                  email_key=True, name_key=True,
-                 use_email=False, use_name=False):
+                 use_email=False, use_name=False,
+                 autocreate=False):
         self.use_email = use_email
         self.authorities = authorities
         self.email_key = email_key
         self.name_key = name_key
         self.use_email = use_email
         self.use_name = use_name
+        self.autocreate = autocreate
         BaseAuth.__init__(self)
 
     def request(self, request, user_obj, **kw):
@@ -87,7 +89,7 @@
             elif user_obj and user_obj.auth_method == self.name:
                 user_obj.valid = False
                 return user_obj, False
-        if u:
+        if u and self.autocreate:
             u.create_or_update(changed)
         if u and u.valid:
             return u, True
--- a/MoinMoin/caching.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/caching.py	Mon Apr 06 12:28:51 2009 +0200
@@ -25,12 +25,7 @@
 
 
 def get_arena_dir(request, arena, scope):
-    if scope == 'page_or_wiki': # XXX DEPRECATED, remove later
-        if isinstance(arena, str):
-            return os.path.join(request.cfg.cache_dir, request.cfg.siteid, arena)
-        else: # arena is in fact a page object
-            return arena.getPagePath('cache', check_create=1)
-    elif scope == 'item': # arena is a Page instance
+    if scope == 'item': # arena is a Page instance
         # we could move cache out of the page directory and store it to cache_dir
         return arena.getPagePath('cache', check_create=1)
     elif scope == 'wiki':
@@ -49,7 +44,7 @@
 
 
 class CacheEntry:
-    def __init__(self, request, arena, key, scope='page_or_wiki', do_locking=True,
+    def __init__(self, request, arena, key, scope='wiki', do_locking=True,
                  use_pickle=False, use_encode=False):
         """ init a cache entry
             @param request: the request object
--- a/MoinMoin/config/__init__.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/config/__init__.py	Mon Apr 06 12:28:51 2009 +0200
@@ -74,7 +74,7 @@
 # Other stuff
 url_schemas = ['http', 'https', 'ftp', 'file',
                'mailto', 'nntp', 'news',
-               'ssh', 'telnet', 'irc', 'ircs', 'xmpp',
+               'ssh', 'telnet', 'irc', 'ircs', 'xmpp', 'mumble',
                'webcal', 'ed2k', 'rootz',
                'notes',
               ]
--- a/MoinMoin/config/_tests/test_configs.py	Mon Apr 06 12:27:11 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-from MoinMoin.config.multiconfig import DefaultConfig
-
-class NoUnderlay(DefaultConfig):
-    data_underlay_dir = None
-
-_tests = [NoUnderlay, ]
-
-class TestConfigs:
-    def testConfigs(self):
-        for cls in _tests:
-            cls.data_dir = self.request.cfg.data_dir
-            # quite a bad hack to make _importPlugin succeed
-            cls('MoinMoin')
--- a/MoinMoin/config/_tests/test_multiconfig.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/config/_tests/test_multiconfig.py	Mon Apr 06 12:28:51 2009 +0200
@@ -32,7 +32,7 @@
             py.test.skip("password_checker is disabled in the configuration, not testing it")
         else:
             for pw, result in self.tests_builtin:
-                pw_error = pw_checker(self.username, pw)
+                pw_error = pw_checker(self.request, self.username, pw)
                 print "%r: %s" % (pw, pw_error)
                 assert result == (pw_error is None)
 
--- a/MoinMoin/config/multiconfig.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/config/multiconfig.py	Mon Apr 06 12:28:51 2009 +0200
@@ -4,6 +4,7 @@
 
     @copyright: 2000-2004 Juergen Hermann <jh@web.de>,
                 2005-2008 MoinMoin:ThomasWaldmann.
+                2008      MoinMoin:JohannesBerg
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -16,7 +17,7 @@
 logging = log.getLogger(__name__)
 
 from MoinMoin import config, error, util, wikiutil
-import MoinMoin.auth as authmodule
+from MoinMoin.auth import MoinAuth
 import MoinMoin.events as events
 from MoinMoin.events import PageChangedEvent, PageRenamedEvent
 from MoinMoin.events import PageDeletedEvent, PageCopiedEvent
@@ -48,12 +49,12 @@
         raise
     except IndentationError, err:
         logging.exception('Your source code / config file is not correctly indented!')
-        msg = '''IndentationError: %(err)s
+        msg = """IndentationError: %(err)s
 
-The configuration files are python modules. Therefore, whitespace is
+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)
@@ -68,7 +69,7 @@
     """ 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.
+    regexes. Later just return the cached regex list.
 
     @rtype: list of tuples of (name, compiled re object)
     @return: url to wiki config name matching list
@@ -124,7 +125,7 @@
         logging.info("using wiki config: %s" % os.path.abspath(module.__file__))
     except ImportError, err:
         logging.exception('Could not import.')
-        msg = '''ImportError: %(err)s
+        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
@@ -134,13 +135,13 @@
 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:
         logging.exception('An exception occured.')
-        msg = '''AttributeError: %(err)s
+        msg = """AttributeError: %(err)s
 
 Could not find required "Config" class in "%(name)s.py".
 
@@ -148,36 +149,18 @@
 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/ -
+config names like e.g. stats.py - because that collides 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)
 
-    # postprocess configuration
-    # 'setuid' special auth method auth method can log out
-    cfg.auth_can_logout = ['setuid']
-    cfg.auth_login_inputs = []
-    found_names = []
-    for auth in cfg.auth:
-        if not auth.name:
-            raise error.ConfigurationError("Auth methods must have a name.")
-        if auth.name in found_names:
-            raise error.ConfigurationError("Auth method names must be unique.")
-        found_names.append(auth.name)
-        if auth.logout_possible and auth.name:
-            cfg.auth_can_logout.append(auth.name)
-        for input in auth.login_inputs:
-            if not input in cfg.auth_login_inputs:
-                cfg.auth_login_inputs.append(input)
-    cfg.auth_have_login = len(cfg.auth_login_inputs) > 0
-
     return cfg
 
 
@@ -221,511 +204,30 @@
     pass
 
 
-class DefaultConfig(object):
-    """ default config values
+class ConfigFunctionality(object):
+    """ Configuration base class with config class behaviour.
 
-        When adding new config attributes, PLEASE use a name with the TOPIC as prefix,
-        so it will sort naturally. E.g. use "actions_excluded", not "excluded_actions".
-
-        Also, please keep it (roughly) sorted (except if you have good reasons to group otherwise).
+        This class contains the functionality for the DefaultConfig
+        class for the benefit of the WikiConfig macro.
     """
 
-    DesktopEdition = False # True gives all local users special powers - ONLY use for MMDE style usage!
-
-    SecurityPolicy = None
-
-    acl_hierarchic = False # True to use hierarchical ACLs
-    # 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 = ['xmlrpc',  # we do not want wiki admins unknowingly offering xmlrpc service
-                        'MyPages',  # only works when used with a non-default SecurityPolicy (e.g. autoadmin)
-                        'CopyPage',  # has questionable behaviour regarding subpages a user can't read, but can copy
-                       ]
-    allow_xslt = False
-    antispam_master_url = "http://master.moinmo.in/?action=xmlrpc2"
-
-    auth = [authmodule.MoinAuth()]
-    # default to http and xmlrpc_applytoken to get old semantics
-    # xmlrpc_applytoken shall be removed once that code is changed
-    # to have proper session handling and use request.handle_auth()
-    auth_methods_trusted = ['http', 'xmlrpc_applytoken']
-
-    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 = True
-    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 = False
-
-    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_secure = None # a secure cookie is not transmitted over unsecure connection
-                         # None = auto-enable secure cookie for https
-                         # True = ever use secure cookie
-                         # False = never use secure cookie
-
-    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
-
-    edit_bar = ['Edit', 'Comments', 'Discussion', 'Info', 'Subscribe', 'Quicklink', 'Attachments', 'ActionsMenu']
-    editor_default = 'text' # which editor is called when nothing is specified
-    editor_force = False # force using the default editor
-    editor_ui = 'freechoice' # which editor links are shown on user interface
-    editor_quickhelp = {
-        # editor markup hints quickhelp
-        # MUST be in wiki markup, even if the help is not for the wiki parser!
-        'wiki': _(u"""\
- Emphasis:: <<Verbatim('')>>''italics''<<Verbatim('')>>; <<Verbatim(''')>>'''bold'''<<Verbatim(''')>>; <<Verbatim(''''')>>'''''bold italics'''''<<Verbatim(''''')>>; <<Verbatim('')>>''mixed ''<<Verbatim(''')>>'''''bold'''<<Verbatim(''')>> and italics''<<Verbatim('')>>; <<Verbatim(----)>> horizontal rule.
- Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; ===== Title 5 =====.
- 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([[target|linktext]])>>.
- 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: *italic* **bold** ``monospace``
-
-Headings: Heading 1  Heading 2  Heading 3
-          =========  ---------  ~~~~~~~~~
-
-Horizontal rule: ----
-
-Links: TrailingUnderscore_ `multi word with backticks`_ external_
-
-.. _external: http://external-site.example.org/foo/
-
-Lists: * bullets; 1., a. numbered items.
-}}}
-(!) For more help, see the
-[[http://docutils.sourceforge.net/docs/user/rst/quickref.html|reStructuredText Quick Reference]].
-"""),
-        'creole': _(u"""\
- Emphasis:: <<Verbatim(//)>>''italics''<<Verbatim(//)>>; <<Verbatim(**)>>'''bold'''<<Verbatim(**)>>; <<Verbatim(**//)>>'''''bold italics'''''<<Verbatim(//**)>>; <<Verbatim(//)>>''mixed ''<<Verbatim(**)>>'''''bold'''<<Verbatim(**)>> and italics''<<Verbatim(//)>>;
- Horizontal Rule:: <<Verbatim(----)>>
- Force Linebreak:: <<Verbatim(\\\\)>>
- Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; ===== Title 5 =====.
- Lists:: * bullets; ** sub-bullets; # numbered items; ## numbered sub items.
- Links:: <<Verbatim([[target]])>>; <<Verbatim([[target|linktext]])>>.
- Tables:: |= header text | cell text | more cell text |;
-
-(!) For more help, see HelpOnEditing or HelpOnCreoleSyntax.
-"""),
-    }
-    edit_locking = 'warn 10' # None, 'warn <timeout mins>', 'lock <timeout mins>'
-    edit_ticketing = True
-    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 {}!
-
-    history_count = (100, 200) # (default_revisions_shown, max_revisions_shown)
-
-    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
-
-    interwikiname = None # our own interwikiname. choose wisely and never change!
-    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
-
-    logo_string = None # can be either just some text or a piece of html shown as "logo"
-
-    log_reverse_dns_lookups = True  # if we do reverse dns lookups for logging hostnames
-                                    # instead of just IPs
-    log_timing = False # log infos about timing of actions, good to analyze load conditions
-
-    mail_from = None # u'Juergen Wiki <noreply@jhwiki.org>'
-    mail_login = None # "user pwd" if you need to use SMTP AUTH when using your mail server
-    mail_smarthost = None # your SMTP mail server
-    mail_sendmail = None # "/usr/sbin/sendmail -t -i" to not use SMTP, but sendmail
-
-    mail_import_secret = "" # a shared secret also known to the mail importer xmlrpc script
-    mail_import_subpage_template = u"$from-$date-$subject" # used for mail import
-    mail_import_pagename_search = ['subject', 'to', ] # where to look for target pagename (and in which order)
-    mail_import_pagename_envelope = u"%s" # use u"+ %s/" to add "+ " and "/" automatically
-    mail_import_pagename_regex = r'\[\[([^\]]*)\]\]' # how to find/extract the pagename from the subject
-    mail_import_wiki_addrs = [] # the e-mail addresses for e-mails that should go into the wiki
-
-    # some dangerous mimetypes (we don't use "content-disposition: inline" for them when a user
-    # downloads such attachments, because the browser might execute e.g. Javascript contained
-    # in the HTML and steal your moin session cookie or do other nasty stuff)
-    mimetypes_xss_protect = [
-        'text/html',
-        'application/x-shockwave-flash',
-        'application/xhtml+xml',
-    ]
-
-    mimetypes_embed = [
-        'application/x-dvi',
-        'application/postscript',
-        'application/pdf',
-        'application/ogg',
-        'application/vnd.visio',
-        'image/x-ms-bmp',
-        'image/svg+xml',
-        'image/tiff',
-        'image/x-photoshop',
-        'audio/mpeg',
-        'audio/midi',
-        'audio/x-wav',
-        'video/fli',
-        'video/mpeg',
-        'video/quicktime',
-        'video/x-msvideo',
-        'chemical/x-pdb',
-        'x-world/x-vrml',
-    ]
-
-
-    navi_bar = [u'RecentChanges', u'FindPage', u'HelpContents', ]
-    nonexist_qm = False
-
-    notification_bot_uri = None # uri of the jabber bot
-
-    # OpenID server support
-    openid_server_enabled = False
-    openid_server_restricted_users_group = None
-    openid_server_enable_user = False
-
-    page_credits = [
-        # Feel free to add other credits, but PLEASE do NOT change or remove
-        # the following links - you help us by keeping them "as is":
-        '<a href="http://moinmo.in/" title="This site uses the MoinMoin Wiki software.">MoinMoin Powered</a>',
-        '<a href="http://moinmo.in/Python" title="MoinMoin is written in Python.">Python Powered</a>',
-
-        # Optional credits:
-        # if you think it can be maybe misunderstood as applying to content or topic of your wiki,
-        # feel free to remove this one:
-        '<a href="http://moinmo.in/GPL" title="MoinMoin is GPL licensed.">GPL licensed</a>',
-
-        # if you don't need/want to check the html output, feel free to remove this one:
-        '<a href="http://validator.w3.org/check?uri=referer" title="Click here to validate this page.">Valid HTML 4.01</a>',
-        ]
-
-    # you can put some pieces of html at specific places into the theme output:
-    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'
-
-    # the following regexes should match the complete name when used in free text
-    # the group 'all' shall match all, while the group 'key' shall match the key only
-    # e.g. CategoryFoo -> group 'all' ==  CategoryFoo, group 'key' == Foo
-    # moin's code will add ^ / $ at beginning / end when needed
-    page_category_regex =  ur'(?P<all>Category(?P<key>(?!Template)\S+))'
-    page_dict_regex = ur'(?P<all>(?P<key>\S+)Dict)'
-    page_group_regex = ur'(?P<all>(?P<key>\S+)Group)'
-    page_template_regex = ur'(?P<all>(?P<key>\S+)Template)'
-
-    page_license_enabled = False
-    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           pagekey, querystr dict, title, icon-key
-        'diff':        ('page', {'action': 'diff'}, _("Diffs"), "diff"),
-        'info':        ('page', {'action': 'info'}, _("Info"), "info"),
-        'edit':        ('page', {'action': 'edit'}, _("Edit"), "edit"),
-        'unsubscribe': ('page', {'action': 'unsubscribe'}, _("UnSubscribe"), "unsubscribe"),
-        'subscribe':   ('page', {'action': 'subscribe'}, _("Subscribe"), "subscribe"),
-        'raw':         ('page', {'action': 'raw'}, _("Raw"), "raw"),
-        'xml':         ('page', {'action': 'show', 'mimetype': 'text/xml'}, _("XML"), "xml"),
-        'print':       ('page', {'action': 'print'}, _("Print"), "print"),
-        'view':        ('page', {}, _("View"), "view"),
-        'up':          ('page_parent_page', {}, _("Up"), "up"),
-        }
-
-
-    def password_checker(username, password):
-        """ Check if a password is secure enough.
-            We use a built-in check to get rid of the worst passwords.
-
-            We do NOT use cracklib / python-crack here any more because it is
-            not thread-safe (we experienced segmentation faults when using it).
-
-            If you don't want to check passwords, use password_checker = None.
-
-            @return: None if there is no problem with the password,
-                     some string with an error msg, if the password is problematic.
-        """
-
-        try:
-            # in any case, do a very simple built-in check to avoid the worst passwords
-            if len(password) < 6:
-                raise ValueError("Password too short.")
-            if len(set(password)) < 4:
-                raise ValueError("Password has not enough different characters.")
-
-            username_lower = username.lower()
-            password_lower = password.lower()
-            if username in password or password in username or \
-               username_lower in password_lower or password_lower in username_lower:
-                raise ValueError("Password too easy (containment).")
-
-            keyboards = (ur"`1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./", # US kbd
-                         ur"^1234567890ߴqwertzuiop+asdfghjkl#yxcvbnm,.-", # german kbd
-                        ) # add more keyboards!
-            for kbd in keyboards:
-                rev_kbd = kbd[::-1]
-                if password in kbd or password in rev_kbd or \
-                   password_lower in kbd or password_lower in rev_kbd:
-                    raise ValueError("Password too easy (kbd sequence)")
-            return None
-        except ValueError, err:
-            return str(err)
-
-    password_checker = staticmethod(password_checker)
-
-    quicklinks_default = [] # preload user quicklinks with this page list
-
-    refresh = None # (minimum_delay, type), e.g.: (2, 'internal')
-    rss_cache = 60 # suggested caching time for RecentChanges RSS, in seconds
-
-    search_results_per_page = 25
-
-    session_handler = session.DefaultSessionHandler()
-    session_id_handler = session.MoinCookieSessionIDHandler()
-
-    secrets = None  # if wiki admin does not set it, will get calculated from some cfg values
-
-    shared_intermap = None # can be string or list of strings (filenames)
-
-    show_hosts = True # show hostnames on RecentChanges / info/history action
-    show_interwiki = False # show our interwiki name (usually in front of the page name)
-    show_names = True # show editor names on RecentChanges / info/history action
-    show_section_numbers = 0 # enumerate sections (headlines) by default?
-    show_timings = False # show some timing stats (usually in the footer)
-    show_version = False # show moin version info / (C) (depends on theme)
-
-    sistersites = [
-        #('Self', 'http://localhost:8080/?action=sisterpages'),
-        #('EmacsWiki', 'http://www.emacswiki.org/cgi-bin/test?action=sisterpages'),
-        #('JspWiki', 'http://www.jspwiki.org/SisterSites.jsp'),
-    ] # list of (sistersitename, sisterpagelistfetchurl)
-
-    siteid = 'default'
-    sitename = u'Untitled Wiki' # Wiki identity
-
-    stylesheets = [] # list of tuples (media, csshref) to insert after theme css, before user css
-
-    _subscribable_events = None # A list of event types that user can subscribe to
-    subscribed_pages_default = [] # preload user subscribed pages with this page list
-    email_subscribed_events_default = [
-        PageChangedEvent.__name__,
-        PageRenamedEvent.__name__,
-        PageDeletedEvent.__name__,
-        PageCopiedEvent.__name__,
-        PageRevertedEvent.__name__,
-        FileAttachedEvent.__name__,
-    ]
-    jabber_subscribed_events_default = []
-
-    superuser = [] # list of unicode user names that have super powers :)
-
-    supplementation_page = False # use supplementation pages (show a link in the theme)?
-    supplementation_page_name = u'Discussion' # name of suppl. subpage
-    supplementation_page_template = u'DiscussionTemplate' # name of template used to create suppl. pages
-
-    surge_action_limits = {# allow max. <count> <action> requests per <dt> secs
-        # action: (count, dt)
-        'all': (30, 30), # all requests (except cache/AttachFile action) count for this limit
-        'default': (30, 60), # default limit for actions without a specific limit
-        'show': (30, 60),
-        'recall': (10, 120),
-        'raw': (20, 40),  # some people use this for css
-        'diff': (30, 60),
-        'fullsearch': (10, 120),
-        'edit': (30, 300), # can be lowered after making preview different from edit
-        'rss_rc': (1, 60),
-        # The following actions are often used for images - to avoid pages with lots of images
-        # (like photo galleries) triggering surge protection, we assign rather high limits:
-        'AttachFile': (90, 60),
-        'cache': (600, 30), # cache action is very cheap/efficient
-    }
-    surge_lockout_time = 3600 # secs you get locked out when you ignore warnings
-
-    textchas = None # a data structure with site-specific questions/answers, see HelpOnTextChas
-    textchas_disabled_group = None # e.g. u'NoTextChasGroup' if you are a member of this group, you don't get textchas
-
-    theme_default = 'modern'
-    theme_force = False
-
-    traceback_show = True # if True, tracebacks are displayed in the web browser
-    traceback_log_dir = None # if set to a directory path, tracebacks are written to files there
-
-    trail_size = 5 # number of recently visited pagenames shown in the trail display
-    tz_offset = 0.0 # default time zone offset in hours from UTC
-
-    # a regex of HTTP_USER_AGENTS that should be excluded from logging
-    # and receive a FORBIDDEN for anything except viewing a page
-    # list must not contain 'java' because of twikidraw wanting to save drawing uses this useragent
-    ua_spiders = ('archiver|cfetch|charlotte|crawler|curl|gigabot|googlebot|heritrix|holmes|htdig|httrack|httpunit|'
-                  'intelix|jeeves|larbin|leech|libwww-perl|linkbot|linkmap|linkwalk|litefinder|mercator|'
-                  'microsoft.url.control|mirror| mj12bot|msnbot|msrbot|neomo|nutbot|omniexplorer|puf|robot|scooter|seekbot|'
-                  'sherlock|slurp|sitecheck|snoopy|spider|teleport|twiceler|voilabot|voyager|webreaper|wget|yeti')
-
-    unzip_single_file_size = 2.0 * 1000 ** 2
-    unzip_attachments_space = 200.0 * 1000 ** 2
-    unzip_attachments_count = 101 # 1 zip file + 100 files contained in it
-
-    url_mappings = {}
-
-    # url_prefix is DEPRECATED and not used any more by the code.
-    # it confused many people by its name and default value of '/wiki' to the
-    # wrong conclusion that it is the url of the wiki (the dynamic) stuff,
-    # but it was used to address the static stuff (images, css, js).
-    # Thus we use the more clear url_prefix_static ['/moin_staticVVV'] setting now.
-    # For a limited time, we still look at url_prefix - if it is not None, we
-    # copy the value to url_prefix_static to ease transition.
-    url_prefix = None
-
-    # includes the moin version number, so we can have a unlimited cache lifetime
-    # for the static stuff. if stuff changes on version upgrade, url will change
-    # immediately and we have no problem with stale caches.
-    url_prefix_static = config.url_prefix_static
-    url_prefix_local = None # if None, use same value as url_prefix_static.
-                            # must be same site as wiki engine (for e.g. JS permissions)
-
-    # we could prefix actions to be able to exclude them by robots.txt:
-    #url_prefix_action = 'action' # no leading or trailing '/'
-    url_prefix_action = None # compatiblity
-
-    # allow disabling certain userpreferences plugins
-    userprefs_disabled = []
-
-    user_autocreate = False # do we auto-create user profiles
-    user_email_unique = True # do we check whether a user's email is unique?
-    user_jid_unique = True # do we check whether a user's email is unique?
-
-    user_homewiki = u'Self' # interwiki name for where user homepages are located
-
-    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_comments', lambda _: _('Show comment sections')),
-        ('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')),
-
-        ('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_comments':       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,
-                             }
-
-    # don't let the user change those
-    # user_checkbox_disable = ['disabled']
-    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 FirstnameLastname)")),
-        ('aliasname', _('Alias-Name'), "text", "36", ''),
-        ('email', _('Email'), "text", "36", ''),
-        ('jid', _('Jabber ID'), "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': '',
-        'jid': '',
-        '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', 'password', 'password2', 'auth_method', 'auth_attribs', ]
-
-    xapian_search = False
-    xapian_index_dir = None
-    xapian_stemming = False
-    xapian_index_history = False
+    # attributes of this class that should not be shown
+    # in the WikiConfig() macro.
+    cfg_mtime = None
+    siteid = None
+    cache = None
+    mail_enabled = None
+    jabber_enabled = None
+    auth_can_logout = None
+    auth_have_login = None
+    auth_login_inputs = None
+    _site_plugin_lists = None
+    _iwid = None
+    _iwid_full = None
+    xapian_searchers = None
+    moinmoin_dir = None
+    # will be lazily loaded by interwiki code when needed (?)
+    shared_intermap_files = None
 
     def __init__(self, siteid):
         """ Init Config instance """
@@ -763,7 +265,7 @@
         self.cache.page_group_regexact = re.compile(u'^%s$' % self.page_group_regex, re.UNICODE)
         self.cache.page_template_regexact = re.compile(u'^%s$' % self.page_template_regex, re.UNICODE)
 
-        self.cache.ua_spiders = self.ua_spiders and re.compile(self.ua_spiders, re.I)
+        self.cache.ua_spiders = self.ua_spiders and re.compile(self.ua_spiders, re.IGNORECASE)
 
         self._check_directories()
 
@@ -799,13 +301,29 @@
 
         # post process
 
+        # 'setuid' special auth method auth method can log out
+        self.auth_can_logout = ['setuid']
+        self.auth_login_inputs = []
+        found_names = []
+        for auth in self.auth:
+            if not auth.name:
+                raise error.ConfigurationError("Auth methods must have a name.")
+            if auth.name in found_names:
+                raise error.ConfigurationError("Auth method names must be unique.")
+            found_names.append(auth.name)
+            if auth.logout_possible and auth.name:
+                self.auth_can_logout.append(auth.name)
+            for input in auth.login_inputs:
+                if not input in self.auth_login_inputs:
+                    self.auth_login_inputs.append(input)
+        self.auth_have_login = len(self.auth_login_inputs) > 0
+
         # internal dict for plugin `modules' lists
         self._site_plugin_lists = {}
 
         # 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]
 
         # check if python-xapian is installed
         if self.xapian_search:
@@ -820,27 +338,16 @@
 
         # 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
+        self.mail_enabled = self.mail_enabled and True or False
 
         # check if jabber bot is available and set flag:
         self.jabber_enabled = self.notification_bot_uri is not None
 
         # if we are to use the jabber bot, instantiate a server object for future use
         if self.jabber_enabled:
-
-            errmsg = "You must set a (long) secret string to send notifications!"
-            try:
-                if not self.secret:
-                    raise error.ConfigurationError(errmsg)
-            except AttributeError, err:
-                raise error.ConfigurationError(errmsg)
-
             from xmlrpclib import Server
             self.notification_server = Server(self.notification_bot_uri, )
 
-        if self.secrets is None:  # Note: this is 'secrets' (with s at the end), not 'secret' (as above)
-                                  # This stuff is already cleaned up in 1.8 repo...
-            self.secrets = self.calc_secrets()
-
         # Cache variables for the properties below
         self._iwid = self._iwid_full = self._meta_dict = None
 
@@ -848,9 +355,6 @@
         self.cache.acl_rights_default = AccessControlList(self, [self.acl_rights_default])
         self.cache.acl_rights_after = AccessControlList(self, [self.acl_rights_after])
 
-        if self.url_prefix is not None: # remove this code when url_prefix setting is removed
-            self.url_prefix_static = self.url_prefix
-
         action_prefix = self.url_prefix_action
         if action_prefix is not None and action_prefix.endswith('/'): # make sure there is no trailing '/'
             self.url_prefix_action = action_prefix[:-1]
@@ -858,6 +362,36 @@
         if self.url_prefix_local is None:
             self.url_prefix_local = self.url_prefix_static
 
+        if self.url_prefix_fckeditor is None:
+            self.url_prefix_fckeditor = self.url_prefix_local + '/applets/FCKeditor'
+
+        if self.secrets is None:  # admin did not setup a real secret, so make up something
+            self.secrets = self.calc_secrets()
+
+        secret_key_names = ['action/cache', 'wikiutil/tickets', 'xmlrpc/ProcessMail', 'xmlrpc/RemoteScript', ]
+        if self.jabber_enabled:
+            secret_key_names.append('jabberbot')
+
+        secret_min_length = 10
+        if isinstance(self.secrets, str):
+            if len(self.secrets) < secret_min_length:
+                raise error.ConfigurationError("The secrets = '...' wiki config setting is a way too short string (minimum length is %d chars)!" % (
+                    secret_min_length))
+            # for lazy people: set all required secrets to same value
+            secrets = {}
+            for key in secret_key_names:
+                secrets[key] = self.secrets
+            self.secrets = secrets
+
+        # we check if we have all secrets we need and that they have minimum length
+        for secret_key_name in secret_key_names:
+            try:
+                secret = self.secrets[secret_key_name]
+                if len(secret) < secret_min_length:
+                    raise ValueError
+            except (KeyError, ValueError):
+                raise error.ConfigurationError("You must set a (at least %d chars long) secret string for secrets['%s']!" % (
+                    secret_min_length, secret_key_name))
 
     def calc_secrets(self):
         """ make up some 'secret' using some config values """
@@ -872,9 +406,10 @@
                 secret += repr(var)
         return secret
 
+    _meta_dict = None
     def load_meta_dict(self):
         """ The meta_dict contains meta data about the wiki instance. """
-        if getattr(self, "_meta_dict", None) is None:
+        if self._meta_dict is None:
             self._meta_dict = wikiutil.MetaDict(os.path.join(self.data_dir, 'meta'), self.cache_dir)
         return self._meta_dict
     meta_dict = property(load_meta_dict)
@@ -889,25 +424,13 @@
     iwid = make_iwid_property("_iwid")
     iwid_full = make_iwid_property("_iwid_full")
 
-    # lazily load a list of events a user can subscribe to
-    def make_subscribable_events_prop():
-        def getter(self):
-            if getattr(self, "_subscribable_events", None) is None:
-                self._subscribable_events = events.get_subscribable_events()
-            return getattr(self, "_subscribable_events")
-
-        def setter(self, new_events):
-            self._subscribable_events = new_events
-
-        return property(getter, setter)
-    subscribable_events = make_subscribable_events_prop()
-
     # lazily create a list of event handlers
+    _event_handlers = None
     def make_event_handlers_prop():
         def getter(self):
-            if getattr(self, "_event_handlers", None) is None:
+            if self._event_handlers is None:
                 self._event_handlers = events.get_handlers(self)
-            return getattr(self, "_event_handlers")
+            return self._event_handlers
 
         def setter(self, new_handlers):
             self._event_handlers = new_handlers
@@ -972,13 +495,13 @@
         config files.
         """
         charset = 'utf-8'
-        message = u'''
+        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', 'interwikiname', 'user_homewiki', 'logo_string', 'navi_bar',
@@ -1025,7 +548,7 @@
 
             path_pages = os.path.join(path, "pages")
             if not (os.path.isdir(path_pages) and os.access(path_pages, mode)):
-                msg = '''
+                msg = """
 %(attr)s "%(path)s" does not exist, or has incorrect ownership or
 permissions.
 
@@ -1035,50 +558,60 @@
 
 It is recommended to use absolute paths and not relative paths. Check
 also the spelling of the directory name.
-''' % {'attr': attr, 'path': path, }
+""" % {'attr': attr, 'path': path, }
                 raise error.ConfigurationError(msg)
 
     def _loadPluginModule(self):
-        """ import plugin module under configname.plugin
+        """
+        import all plugin modules
 
         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.
+        Since each configured plugin path has unique plugins, we load the
+        plugin packages as "moin_plugin_<sha1(path)>.plugin".
         """
         import imp
+        from MoinMoin.support.python_compatibility import hash_new
 
-        name = self.siteid + '.plugin'
+        plugin_dirs = [self.plugin_dir] + self.plugin_dirs
+        self._plugin_modules = []
+
         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()
+                for pdir in plugin_dirs:
+                    csum = 'p_%s' % hash_new('sha1', pdir).hexdigest()
+                    modname = '%s.%s' % (self.siteid, csum)
+                    # If the module is not loaded, try to load it
+                    if not modname in sys.modules:
+                        # Find module on disk and try to load - slow!
+                        abspath = os.path.abspath(pdir)
+                        parent_dir, pname = os.path.split(abspath)
+                        fp, path, info = imp.find_module(pname, [parent_dir])
+                        try:
+                            # Load the module and set in sys.modules
+                            module = imp.load_module(modname, fp, path, info)
+                            setattr(sys.modules[self.siteid], 'csum', module)
+                        finally:
+                            # Make sure fp is closed properly
+                            if fp:
+                                fp.close()
+                    if modname not in self._plugin_modules:
+                        self._plugin_modules.append(modname)
             finally:
                 imp.release_lock()
         except ImportError, err:
-            msg = '''
-Could not import plugin package "%(path)s/plugin" because of ImportError:
+            msg = """
+Could not import plugin package "%(path)s" 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,
+""" % {
+    'path': pdir,
     'err': str(err),
 }
             raise error.ConfigurationError(msg)
@@ -1098,6 +631,599 @@
         """ Make it possible to access a config object like a dict """
         return getattr(self, item)
 
+
+class DefaultConfig(ConfigFunctionality):
+    """ Configuration base class with default config values
+        (added below)
+    """
+    # Do not add anything into this class. Functionality must
+    # be added above to avoid having the methods show up in
+    # the WikiConfig macro. Settings must be added below to
+    # the options dictionary.
+
+
+def _default_password_checker(cfg, request, username, password):
+    """ Check if a password is secure enough.
+        We use a built-in check to get rid of the worst passwords.
+
+        We do NOT use cracklib / python-crack here any more because it is
+        not thread-safe (we experienced segmentation faults when using it).
+
+        If you don't want to check passwords, use password_checker = None.
+
+        @return: None if there is no problem with the password,
+                 some unicode object with an error msg, if the password is problematic.
+    """
+    _ = request.getText
+    # in any case, do a very simple built-in check to avoid the worst passwords
+    if len(password) < 6:
+        return _("Password is too short.")
+    if len(set(password)) < 4:
+        return _("Password has not enough different characters.")
+
+    username_lower = username.lower()
+    password_lower = password.lower()
+    if username in password or password in username or \
+       username_lower in password_lower or password_lower in username_lower:
+        return _("Password is too easy (password contains name or name contains password).")
+
+    keyboards = (ur"`1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./", # US kbd
+                 ur"^1234567890ߴqwertzuiop+asdfghjkl#yxcvbnm,.-", # german kbd
+                ) # add more keyboards!
+    for kbd in keyboards:
+        rev_kbd = kbd[::-1]
+        if password in kbd or password in rev_kbd or \
+           password_lower in kbd or password_lower in rev_kbd:
+            return _("Password is too easy (keyboard sequence).")
+    return None
+
+
+class DefaultExpression(object):
+    def __init__(self, exprstr):
+        self.text = exprstr
+        self.value = eval(exprstr)
+
+
+#
+# Options that are not prefixed automatically with their
+# group name, see below (at the options dict) for more
+# information on the layout of this structure.
+#
+options_no_group_name = {
+  # ==========================================================================
+  'session': ('Session settings', "Session-related settings, see HelpOnSessions.", (
+    ('session_handler', DefaultExpression('session.DefaultSessionHandler()'),
+     "See HelpOnSessions."),
+    ('session_id_handler', DefaultExpression('session.MoinCookieSessionIDHandler()'),
+     "Only used by the DefaultSessionHandler, see HelpOnSessions."),
+    ('cookie_secure', None,
+     'Use secure cookie. (None = auto-enable secure cookie for https, True = ever use secure cookie, False = never use secure cookie).'),
+    ('cookie_domain', None,
+     'Domain used in the session cookie. (None = do not specify domain).'),
+    ('cookie_path', None,
+     'Path used in the session cookie (None = auto-detect).'),
+    ('cookie_lifetime', 12,
+     'Session lifetime [h] of logged-in users (see HelpOnSessions for details).'),
+    ('anonymous_session_lifetime', None,
+     'Session lifetime [h] of users who are not logged in (None = disable anon sessions).'),
+  )),
+  # ==========================================================================
+  'auth': ('Authentication / Authorization / Security settings', None, (
+    ('superuser', [],
+     "List of trusted user names with wiki system administration super powers (not to be confused with ACL admin rights!). Used for e.g. software installation, language installation via SystemPagesSetup and more. See also HelpOnSuperUser."),
+    ('auth', DefaultExpression('[MoinAuth()]'),
+     "list of auth objects, to be called in this order (see HelpOnAuthentication)"),
+    ('auth_methods_trusted', ['http', 'xmlrpc_applytoken'],
+     'authentication methods for which users should be included in the special "Trusted" ACL group.'),
+    ('secrets', None, """Either a long shared secret string used for multiple purposes or a dict {"purpose": "longsecretstring", ...} for setting up different shared secrets for different purposes. If you don't setup own secret(s), a secret string will be auto-generated from other config settings."""),
+    ('DesktopEdition',
+     False,
+     "if True, give all local users special powers - ''only use this for a local desktop wiki!''"),
+    ('SecurityPolicy',
+     None,
+     "Class object hook for implementing security restrictions or relaxations"),
+    ('actions_excluded',
+     ['xmlrpc',  # we do not want wiki admins unknowingly offering xmlrpc service
+      'MyPages',  # only works when used with a non-default SecurityPolicy (e.g. autoadmin)
+      'CopyPage',  # has questionable behaviour regarding subpages a user can't read, but can copy
+     ],
+     "Exclude unwanted actions (list of strings)"),
+
+    ('allow_xslt', False,
+     "if True, enables XSLT processing via 4Suite (note that this enables anyone with enough know-how to insert '''arbitrary HTML''' into your wiki, which is why it defaults to `False`)"),
+
+    ('password_checker', DefaultExpression('_default_password_checker'),
+     'checks whether a password is acceptable (default check is length >= 6, at least 4 different chars, no keyboard sequence, not username used somehow (you can switch this off by using `None`)'),
+
+  )),
+  # ==========================================================================
+  'spam_leech_dos': ('Anti-Spam/Leech/DOS',
+  'These settings help limiting ressource usage and avoiding abuse.',
+  (
+    ('hosts_deny', [], "List of denied IPs; if an IP ends with a dot, it denies a whole subnet (class A, B or C)"),
+    ('surge_action_limits',
+     {# allow max. <count> <action> requests per <dt> secs
+        # action: (count, dt)
+        'all': (30, 30), # all requests (except cache/AttachFile action) count for this limit
+        'default': (30, 60), # default limit for actions without a specific limit
+        'show': (30, 60),
+        'recall': (10, 120),
+        'raw': (20, 40),  # some people use this for css
+        'diff': (30, 60),
+        'fullsearch': (10, 120),
+        'edit': (30, 300), # can be lowered after making preview different from edit
+        'rss_rc': (1, 60),
+        # The following actions are often used for images - to avoid pages with lots of images
+        # (like photo galleries) triggering surge protection, we assign rather high limits:
+        'AttachFile': (90, 60),
+        'cache': (600, 30), # cache action is very cheap/efficient
+     },
+     "Surge protection tries to deny clients causing too much load/traffic, see /SurgeProtection."),
+    ('surge_lockout_time', 3600, "time [s] someone gets locked out when ignoring the warnings"),
+
+    ('textchas', None,
+     "Spam protection setup using site-specific questions/answers, see HelpOnTextChas."),
+    ('textchas_disabled_group', None,
+     "Name of a group of trusted users who do not get asked TextCha questions."),
+
+    ('antispam_master_url', "http://master.moinmo.in/?action=xmlrpc2",
+     "where antispam security policy fetches spam pattern updates (if it is enabled)"),
+
+    # a regex of HTTP_USER_AGENTS that should be excluded from logging
+    # and receive a FORBIDDEN for anything except viewing a page
+    # list must not contain 'java' because of twikidraw wanting to save drawing uses this useragent
+    ('ua_spiders',
+     ('archiver|cfetch|charlotte|crawler|curl|gigabot|googlebot|heritrix|holmes|htdig|httrack|httpunit|'
+      'intelix|jeeves|larbin|leech|libwww-perl|linkbot|linkmap|linkwalk|litefinder|mercator|'
+      'microsoft.url.control|mirror| mj12bot|msnbot|msrbot|neomo|nutbot|omniexplorer|puf|robot|scooter|seekbot|'
+      'sherlock|slurp|sitecheck|snoopy|spider|teleport|twiceler|voilabot|voyager|webreaper|wget|yeti'),
+     "A regex of HTTP_USER_AGENTs that should be excluded from logging and are not allowed to use actions."),
+
+    ('unzip_single_file_size', 2.0 * 1000 ** 2,
+     "max. size of a single file in the archive which will be extracted [bytes]"),
+    ('unzip_attachments_space', 200.0 * 1000 ** 2,
+     "max. total amount of bytes can be used to unzip files [bytes]"),
+    ('unzip_attachments_count', 101,
+     "max. number of files which are extracted from the zip file"),
+  )),
+  # ==========================================================================
+  'style': ('Style / Theme / UI related',
+  'These settings control how the wiki user interface will look like.',
+  (
+    ('sitename', u'Untitled Wiki',
+     "Short description of your wiki site, displayed below the logo on each page, and used in RSS documents as the channel title [Unicode]"),
+    ('interwikiname', None, "unique and stable InterWiki name (prefix, moniker) of the site [Unicode], or None"),
+    ('logo_string', None, "The wiki logo top of page, HTML is allowed (`<img>` is possible as well) [Unicode]"),
+    ('html_pagetitle', None, "Allows you to set a specific HTML page title (if None, it defaults to the value of `sitename`)"),
+    ('navi_bar', [u'RecentChanges', u'FindPage', u'HelpContents', ],
+     'Most important page names. Users can add more names in their quick links in user preferences. To link to URL, use `u"[[url|link title]]"`, to use a shortened name for long page name, use `u"[[LongLongPageName|title]]"`. [list of Unicode strings]'),
+
+    ('theme_default', 'modern',
+     "the name of the theme that is used by default (see HelpOnThemes)"),
+    ('theme_force', False,
+     "if True, do not allow to change the theme"),
+
+    ('stylesheets', [],
+     "List of tuples (media, csshref) to insert after theme css, before user css, see HelpOnThemes."),
+
+    ('supplementation_page', False,
+     "if True, show a link to the supplementation page in the theme"),
+    ('supplementation_page_name', u'Discussion',
+     "default name of the supplementation (sub)page [unicode]"),
+    ('supplementation_page_template', u'DiscussionTemplate',
+     "default template used for creation of the supplementation page [unicode]"),
+
+    ('interwiki_preferred', [], "In dialogues, show those wikis at the top of the list."),
+    ('sistersites', [], "list of tuples `('WikiName', 'sisterpagelist_fetch_url')`"),
+
+    ('trail_size', 5,
+     "Number of pages in the trail of visited pages"),
+
+    ('page_footer1', '', "Custom HTML markup sent ''before'' the system footer."),
+    ('page_footer2', '', "Custom HTML markup sent ''after'' the system footer."),
+    ('page_header1', '', "Custom HTML markup sent ''before'' the system header / title area but after the body tag."),
+    ('page_header2', '', "Custom HTML markup sent ''after'' the system header / title area (and body tag)."),
+
+    ('changed_time_fmt', '%H:%M', "Time format used on Recent``Changes for page edits within the last 24 hours"),
+    ('date_fmt', '%Y-%m-%d', "System date format, used mostly in Recent``Changes"),
+    ('datetime_fmt', '%Y-%m-%d %H:%M:%S', 'Default format for dates and times (when the user has no preferences or chose the "default" date format)'),
+    ('chart_options', None, "If you have gdchart, use something like chart_options = {'width': 720, 'height': 540}"),
+
+    ('edit_bar', ['Edit', 'Comments', 'Discussion', 'Info', 'Subscribe', 'Quicklink', 'Attachments', 'ActionsMenu'],
+     'list of edit bar entries'),
+    ('history_count', (100, 200), "number of revisions shown for info/history action (default_count_shown, max_count_shown)"),
+
+    ('show_hosts', True,
+     "if True, show host names and IPs. Set to False to hide them."),
+    ('show_interwiki', False,
+     "if True, let the theme display your interwiki name"),
+    ('show_names', True,
+     "if True, show user names in the revision history and on Recent``Changes. Set to False to hide them."),
+    ('show_section_numbers', False,
+     'show section numbers in headings by default'),
+    ('show_timings', False, "show some timing values at bottom of a page"),
+    ('show_version', False, "show moin's version at the bottom of a page"),
+    ('traceback_show', True,
+     "if True, show debug tracebacks to users when moin crashes"),
+
+    ('page_credits',
+     [
+       '<a href="http://moinmo.in/" title="This site uses the MoinMoin Wiki software.">MoinMoin Powered</a>',
+       '<a href="http://moinmo.in/Python" title="MoinMoin is written in Python.">Python Powered</a>',
+       '<a href="http://moinmo.in/GPL" title="MoinMoin is GPL licensed.">GPL licensed</a>',
+       '<a href="http://validator.w3.org/check?uri=referer" title="Click here to validate this page.">Valid HTML 4.01</a>',
+     ],
+     'list with html fragments with logos or strings for crediting.'),
+
+    # 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", ],
+     'list of icons to show in iconbar, valid values are only those in page_icons_table. Available only in classic theme.'),
+
+    # Standard buttons in the iconbar
+    ('page_icons_table',
+     {
+        # key           pagekey, querystr dict, title, icon-key
+        'diff': ('page', {'action': 'diff'}, _("Diffs"), "diff"),
+        'info': ('page', {'action': 'info'}, _("Info"), "info"),
+        'edit': ('page', {'action': 'edit'}, _("Edit"), "edit"),
+        'unsubscribe': ('page', {'action': 'unsubscribe'}, _("UnSubscribe"), "unsubscribe"),
+        'subscribe': ('page', {'action': 'subscribe'}, _("Subscribe"), "subscribe"),
+        'raw': ('page', {'action': 'raw'}, _("Raw"), "raw"),
+        'xml': ('page', {'action': 'show', 'mimetype': 'text/xml'}, _("XML"), "xml"),
+        'print': ('page', {'action': 'print'}, _("Print"), "print"),
+        'view': ('page', {}, _("View"), "view"),
+        'up': ('page_parent_page', {}, _("Up"), "up"),
+     },
+     "dict of {'iconname': (url, title, icon-img-key), ...}. Available only in classic theme."),
+
+  )),
+  # ==========================================================================
+  'editor': ('Editor related', None, (
+    ('editor_default', 'text', "Editor to use by default, 'text' or 'gui'"),
+    ('editor_force', False, "if True, force using the default editor"),
+    ('editor_ui', 'freechoice', "Editor choice shown on the user interface, 'freechoice' or 'theonepreferred'"),
+    ('page_license_enabled', False, 'if True, show a license hint in page editor.'),
+    ('page_license_page', u'WikiLicense', 'Page linked from the license hint. [Unicode]'),
+    ('edit_locking', 'warn 10', "Editor locking policy: `None`, `'warn <timeout in minutes>'`, or `'lock <timeout in minutes>'`"),
+    ('edit_ticketing', True, None),
+    ('edit_rows', 20, "Default height of the edit box"),
+
+  )),
+  # ==========================================================================
+  'paths': ('Paths', None, (
+    ('data_dir', './data/', "Path to the data directory containing your (locally made) wiki pages."),
+    ('data_underlay_dir', './underlay/', "Path to the underlay directory containing distribution system and help pages."),
+    ('cache_dir', None, "Directory for caching, by default computed from `data_dir`/cache."),
+    ('user_dir', None, "Directory for user storage, by default computed to be `data_dir`/user."),
+    ('plugin_dir', None, "Plugin directory, by default computed to be `data_dir`/plugin."),
+    ('plugin_dirs', [], "Additional plugin directories."),
+
+    ('docbook_html_dir', r"/usr/share/xml/docbook/stylesheet/nwalsh/html/",
+     'Path to the directory with the Docbook to HTML XSLT files (optional, used by the docbook parser). The default value is correct for Debian Etch.'),
+    ('shared_intermap', None,
+     "Path to a file containing global InterWiki definitions (or a list of such filenames)"),
+  )),
+  # ==========================================================================
+  'urls': ('URLs', None, (
+    # includes the moin version number, so we can have a unlimited cache lifetime
+    # for the static stuff. if stuff changes on version upgrade, url will change
+    # immediately and we have no problem with stale caches.
+    ('url_prefix_static', config.url_prefix_static,
+     "used as the base URL for icons, css, etc. - includes the moin version number and changes on every release. This replaces the deprecated and sometimes confusing `url_prefix = '/wiki'` setting."),
+    ('url_prefix_local', None,
+     "used as the base URL for some Javascript - set this to a URL on same server as the wiki if your url_prefix_static points to a different server."),
+    ('url_prefix_fckeditor', None,
+     "used as the base URL for FCKeditor - similar to url_prefix_local, but just for FCKeditor."),
+
+    ('url_prefix_action', None,
+     "Use 'action' to enable action URL generation to be compatible with robots.txt. It will generate .../action/info/PageName?action=info then. Recommended for internet wikis."),
+
+    ('notification_bot_uri', None, "URI of the Jabber notification bot."),
+
+    ('url_mappings', {},
+     "lookup table to remap URL prefixes (dict of {{{'prefix': 'replacement'}}}); especially useful in intranets, when whole trees of externally hosted documents move around"),
+
+  )),
+  # ==========================================================================
+  'pages': ('Special page names', None, (
+    ('page_front_page', u'HelpOnLanguages',
+     "Name of the front page. We don't expect you to keep the default. Just read HelpOnLanguages in case you're wondering... [Unicode]"),
+
+    # the following regexes should match the complete name when used in free text
+    # the group 'all' shall match all, while the group 'key' shall match the key only
+    # e.g. CategoryFoo -> group 'all' ==  CategoryFoo, group 'key' == Foo
+    # moin's code will add ^ / $ at beginning / end when needed
+    ('page_category_regex', ur'(?P<all>Category(?P<key>(?!Template)\S+))',
+     'Pagenames exactly matching this regex are regarded as Wiki categories [Unicode]'),
+    ('page_dict_regex', ur'(?P<all>(?P<key>\S+)Dict)',
+     'Pagenames exactly matching this regex are regarded as pages containing variable dictionary definitions [Unicode]'),
+    ('page_group_regex', ur'(?P<all>(?P<key>\S+)Group)',
+     'Pagenames exactly matching this regex are regarded as pages containing group definitions [Unicode]'),
+    ('page_template_regex', ur'(?P<all>(?P<key>\S+)Template)',
+     'Pagenames exactly matching this regex are regarded as pages containing templates for new pages [Unicode]'),
+
+    ('page_local_spelling_words', u'LocalSpellingWords',
+     'Name of the page containing user-provided spellchecker words [Unicode]'),
+  )),
+  # ==========================================================================
+  'user': ('User Preferences related', None, (
+    ('quicklinks_default', [],
+     'List of preset quicklinks for a newly created user accounts. Existing accounts are not affected by this option whereas changes in navi_bar do always affect existing accounts. Preset quicklinks can be removed by the user in the user preferences menu, navi_bar settings not.'),
+    ('subscribed_pages_default', [],
+     "List of pagenames used for presetting page subscriptions for newly created user accounts."),
+
+    ('email_subscribed_events_default',
+     [
+        PageChangedEvent.__name__,
+        PageRenamedEvent.__name__,
+        PageDeletedEvent.__name__,
+        PageCopiedEvent.__name__,
+        PageRevertedEvent.__name__,
+        FileAttachedEvent.__name__,
+     ], None),
+    ('jabber_subscribed_events_default', [], None),
+
+    ('tz_offset', 0.0,
+     "default time zone offset in hours from UTC"),
+
+    ('userprefs_disabled', [],
+     "Disable the listed user preferences plugins."),
+  )),
+  # ==========================================================================
+  'various': ('Various', None, (
+    ('bang_meta', True, 'if True, enable {{{!NoWikiName}}} markup'),
+    ('caching_formats', ['text_html'], "output formats that are cached; set to [] to turn off caching (useful for development)"),
+
+    ('config_check_enabled', False, "if True, check configuration for unknown settings."),
+
+    ('default_markup', 'wiki', 'Default page parser / format (name of module in `MoinMoin.parser`)'),
+
+    ('html_head', '', "Additional <HEAD> tags, see HelpOnThemes."),
+    ('html_head_queries', '<meta name="robots" content="noindex,nofollow">\n',
+     "Additional <HEAD> tags for requests with query strings, like actions."),
+    ('html_head_posts', '<meta name="robots" content="noindex,nofollow">\n',
+     "Additional <HEAD> tags for POST requests."),
+    ('html_head_index', '<meta name="robots" content="index,follow">\n',
+     "Additional <HEAD> tags for some few index pages."),
+    ('html_head_normal', '<meta name="robots" content="index,nofollow">\n',
+     "Additional <HEAD> tags for most normal pages."),
+
+    ('language_default', 'en', "Default language for user interface and page content, see HelpOnLanguages."),
+    ('language_ignore_browser', False, "if True, ignore user's browser language settings, see HelpOnLanguages."),
+
+    ('log_remote_addr', True,
+     "if True, log the remote IP address (and maybe hostname)."),
+    ('log_reverse_dns_lookups', True,
+     "if True, do a reverse DNS lookup on page SAVE. If your DNS is broken, set this to False to speed up SAVE."),
+    ('log_timing', False,
+     "if True, add timing infos to the log output to analyse load conditions"),
+
+    # some dangerous mimetypes (we don't use "content-disposition: inline" for them when a user
+    # downloads such attachments, because the browser might execute e.g. Javascript contained
+    # in the HTML and steal your moin session cookie or do other nasty stuff)
+    ('mimetypes_xss_protect',
+     [
+       'text/html',
+       'application/x-shockwave-flash',
+       'application/xhtml+xml',
+     ],
+     '"content-disposition: inline" isn\'t used for them when a user downloads such attachments'),
+
+    ('mimetypes_embed',
+     [
+       'application/x-dvi',
+       'application/postscript',
+       'application/pdf',
+       'application/ogg',
+       'application/vnd.visio',
+       'image/x-ms-bmp',
+       'image/svg+xml',
+       'image/tiff',
+       'image/x-photoshop',
+       'audio/mpeg',
+       'audio/midi',
+       'audio/x-wav',
+       'video/fli',
+       'video/mpeg',
+       'video/quicktime',
+       'video/x-msvideo',
+       'chemical/x-pdb',
+       'x-world/x-vrml',
+     ],
+     'mimetypes that can be embedded by the [[HelpOnMacros/EmbedObject|EmbedObject macro]]'),
+
+    ('refresh', None,
+     "refresh = (minimum_delay_s, targets_allowed) enables use of `#refresh 5 PageName` processing instruction, targets_allowed must be either `'internal'` or `'external'`"),
+    ('rss_cache', 60, "suggested caching time for Recent''''''Changes RSS, in second"),
+
+    ('search_results_per_page', 25, "Number of hits shown per page in the search results"),
+
+    ('siteid', 'default', None),
+  )),
+}
+
+#
+# The 'options' dict carries default MoinMoin options. The dict is a
+# group name to tuple mapping.
+# Each group tuple consists of the following items:
+#   group section heading, group help text, option list
+#
+# where each 'option list' is a tuple or list of option tuples
+#
+# each option tuple consists of
+#   option name, default value, help text
+#
+# All the help texts will be displayed by the WikiConfigHelp() macro.
+#
+# Unlike the options_no_group_name dict, option names in this dict
+# are automatically prefixed with "group name '_'" (i.e. the name of
+# the group they are in and an underscore), e.g. the 'hierarchic'
+# below creates an option called "acl_hierarchic".
+#
+# If you need to add a complex default expression that results in an
+# object and should not be shown in the __repr__ form in WikiConfigHelp(),
+# you can use the DefaultExpression class, see 'auth' above for example.
+#
+#
+options = {
+    'acl': ('Access control lists',
+    'ACLs control who may do what, see HelpOnAccessControlLists.',
+    (
+      ('hierarchic', False, 'True to use hierarchical ACLs'),
+      ('rights_default', u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write",
+       "ACL used if no ACL is specified on the page"),
+      ('rights_before', u"",
+       "ACL that is processed before the on-page/default ACL"),
+      ('rights_after', u"",
+       "ACL that is processed after the on-page/default ACL"),
+      ('rights_valid', ['read', 'write', 'delete', 'revert', 'admin'],
+       "Valid tokens for right sides of ACL entries."),
+    )),
+
+    'xapian': ('Xapian search', "Configuration of the Xapian based indexed search, see HelpOnXapian.", (
+      ('search', False,
+       "True to enable the fast, indexed search (based on the Xapian search library)"),
+      ('index_dir', None,
+       "Directory where the Xapian search index is stored (None = auto-configure wiki local storage)"),
+      ('stemming', False,
+       "True to enable Xapian word stemmer usage for indexing / searching."),
+      ('index_history', False,
+       "True to enable indexing of non-current page revisions."),
+    )),
+
+    'user': ('Users / User settings', None, (
+      ('email_unique', True,
+       "if True, check email addresses for uniqueness and don't accept duplicates."),
+      ('jid_unique', True,
+       "if True, check Jabber IDs for uniqueness and don't accept duplicates."),
+
+      ('homewiki', u'Self',
+       "interwiki name of the wiki where the user home pages are located [Unicode] - useful if you have ''many'' users. You could even link to nonwiki \"user pages\" if the wiki username is in the target URL."),
+
+      ('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_comments', lambda _: _('Show comment sections')),
+        ('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')),
+
+        ('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:
+       ],
+       "Describes user preferences, see /UserPreferences."),
+
+      ('checkbox_defaults',
+       {
+        'mailto_author': 0,
+        'edit_on_doubleclick': 0,
+        'remember_last_visit': 0,
+        'show_comments': 0,
+        'show_nonexist_qm': False,
+        'show_page_trail': 1,
+        'show_toolbar': 1,
+        'show_topbottom': 0,
+        'show_fancy_diff': 1,
+        'wikiname_add_spaces': 0,
+        'remember_me': 1,
+       },
+       "Defaults for user preferences, see /UserPreferences."),
+
+      ('checkbox_disable', [],
+       "Disable user preferences, see /UserPreferences."),
+
+      ('checkbox_remove', [],
+       "Remove user preferences, see /UserPreferences."),
+
+      ('form_fields',
+       [
+        ('name', _('Name'), "text", "36", _("(Use FirstnameLastname)")),
+        ('aliasname', _('Alias-Name'), "text", "36", ''),
+        ('email', _('Email'), "text", "36", ''),
+        ('jid', _('Jabber ID'), "text", "36", ''),
+        ('css_url', _('User CSS URL'), "text", "40", _('(Leave it empty for disabling user CSS)')),
+        ('edit_rows', _('Editor size'), "text", "3", ''),
+       ],
+       None),
+
+      ('form_defaults',
+       {# key: default - do NOT remove keys from here!
+        'name': '',
+        'aliasname': '',
+        'password': '',
+        'password2': '',
+        'email': '',
+        'jid': '',
+        'css_url': '',
+        'edit_rows': "20",
+       },
+       None),
+
+      ('form_disable', [], "list of field names used to disable user preferences form fields"),
+
+      ('form_remove', [], "list of field names used to remove user preferences form fields"),
+
+      ('transient_fields',
+       ['id', 'valid', 'may', 'auth_username', 'password', 'password2', 'auth_method', 'auth_attribs', ],
+       "User object attributes that are not persisted to permanent storage (internal use)."),
+    )),
+
+    'openid_server': ('OpenID Server',
+        'These settings control the built-in OpenID Identity Provider (server).',
+    (
+      ('enabled', False, "True to enable the built-in OpenID server."),
+      ('restricted_users_group', None, "If set to a group name, the group members are allowed to use the wiki as an OpenID provider. (None = allow for all users)"),
+      ('enable_user', False, "If True, the OpenIDUser processing instruction is allowed."),
+    )),
+
+    'mail': ('Mail settings',
+        'These settings control outgoing and incoming email from and to the wiki.',
+    (
+      ('from', None, "Used as From: address for generated mail."),
+      ('login', None, "'username userpass' for SMTP server authentication (None = don't use auth)."),
+      ('smarthost', None, "Address of SMTP server to use for sending mail (None = don't use SMTP server)."),
+      ('sendmail', None, "sendmail command to use for sending mail (None = don't use sendmail)"),
+
+      ('import_subpage_template', u"$from-$date-$subject", "Create subpages using this template when importing mail."),
+      ('import_pagename_search', ['subject', 'to', ], "Where to look for target pagename specification."),
+      ('import_pagename_envelope', u"%s", "Use this to add some fixed prefix/postfix to the generated target pagename."),
+      ('import_pagename_regex', r'\[\[([^\]]*)\]\]', "Regular expression used to search for target pagename specification."),
+      ('import_wiki_addrs', [], "Target mail addresses to consider when importing mail"),
+    )),
+
+    'backup': ('Backup settings',
+        'These settings control how the backup action works and who is allowed to use it.',
+    (
+      ('compression', 'gz', 'What compression to use for the backup ("gz" or "bz2").'),
+      ('users', [], 'List of trusted user names who are allowed to get a backup.'),
+      ('include', [], 'List of pathes to backup.'),
+      ('exclude', lambda self, filename: False, 'Function f(self, filename) that tells whether a file should be excluded from backup. By default, nothing is excluded.'),
+    )),
+}
+
+def _add_options_to_defconfig(opts, addgroup=True):
+    for groupname in opts:
+        group_short, group_doc, group_opts = opts[groupname]
+        for name, default, doc in group_opts:
+            if addgroup:
+                name = groupname + '_' + name
+            if isinstance(default, DefaultExpression):
+                default = default.value
+            setattr(DefaultConfig, name, default)
+
+_add_options_to_defconfig(options)
+_add_options_to_defconfig(options_no_group_name, False)
+
 # remove the gettext pseudo function
 del _
 
--- a/MoinMoin/conftest.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/conftest.py	Mon Apr 06 12:28:51 2009 +0200
@@ -12,34 +12,28 @@
 classes by the framework.
 
 Tests that require a certain configuration, like section_numbers = 1, must
-use a TestConfig to create the required configuration before the test.
-Deleting the TestConfig instance will restore the previous configuration.
+use a Config class to define the required configuration within the test class.
 
-@copyright: 2005 Nir Soffer, 2007 Alexander Schremmer
+@copyright: 2005 MoinMoin:NirSoffer,
+            2007 MoinMoin:AlexanderSchremmer,
+            2008 MoinMoin:ThomasWaldmann
 @license: GNU GPL, see COPYING for details.
 """
 
 import atexit
-from sys import modules
 import sys
 
 import py
 
-
 rootdir = py.magic.autopath().dirpath()
 moindir = rootdir.join("..")
-
 sys.path.insert(0, str(moindir))
-from MoinMoin._tests import maketestwiki, compat
-modules["unittest"] = compat # evil hack
-
-wikiconfig_dir = str(moindir.join("tests"))
 
 from MoinMoin.support.python_compatibility import set
+from MoinMoin._tests import maketestwiki, wikiconfig
 
 coverage_modules = set()
 
-
 try:
     """
     This code adds support for coverage.py (see
@@ -52,7 +46,7 @@
 
     def report_coverage():
         coverage.stop()
-        module_list = [modules[mod] for mod in coverage_modules]
+        module_list = [sys.modules[mod] for mod in coverage_modules]
         module_list.sort()
         coverage.report(module_list)
 
@@ -70,17 +64,14 @@
     coverage = None
 
 
-def init_test_request(static_state=[False]):
+def init_test_request(given_config=None, static_state=[False]):
     from MoinMoin.request import request_cli
     from MoinMoin.user import User
     from MoinMoin.formatter.text_html import Formatter as HtmlFormatter
     if not static_state[0]:
         maketestwiki.run(True)
         static_state[0] = True
-    if sys.path[0] != wikiconfig_dir:
-        sys.path.insert(0, wikiconfig_dir) # this is a race with py.test's collectors
-                                           # because they modify sys.path as well
-    request = request_cli.Request()
+    request = request_cli.Request(given_config=given_config)
     request.form = request.args = request.setup_args()
     request.user = User(request)
     request.html_formatter = HtmlFormatter(request)
@@ -88,84 +79,6 @@
     return request
 
 
-class TestConfig:
-    """ Custom configuration for unit tests
-
-    Some tests assume a specific configuration, and will fail if the wiki admin
-    changed the configuration. For example, DateTime macro test assume
-    the default datetime_fmt.
-
-    When you set custom values in a TestConfig, the previous values are saved,
-    and when the TestConfig is called specifically, they are restored automatically.
-
-    Typical Usage
-    -------------
-    ::
-        class SomeTest:
-            def setUp(self):
-                self.config = self.TestConfig(defaults=key_list, key=value,...)
-            def tearDown(self):
-                self.config.restore()
-            def testSomething(self):
-                # test that needs those defaults and custom values
-    """
-
-    def __init__(self, request):
-        """ Create temporary configuration for a test
-
-        @param request: current request
-        """
-        self.request = request
-        self.old = {}  # Old config values
-        self.new = []  # New added attributes
-
-    def __call__(self, defaults=(), **custom):
-        """ Initialise a temporary configuration for a test
-
-        @param defaults: list of keys that should use the default value
-        @param custom: other keys using non default values, or new keys
-               that request.cfg does not have already
-        """
-        self.setDefaults(defaults)
-        self.setCustom(**custom)
-
-        return self
-
-    def setDefaults(self, defaults=()):
-        """ Set default values for keys in defaults list
-
-        Non existing default will raise an AttributeError.
-        """
-        from MoinMoin.config import multiconfig
-        for key in defaults:
-            self._setattr(key, getattr(multiconfig.DefaultConfig, key))
-
-    def setCustom(self, **custom):
-        """ Set custom values """
-        for key, value in custom.items():
-            self._setattr(key, value)
-
-    def _setattr(self, key, value):
-        """ Set a new value for key saving new added keys """
-        if hasattr(self.request.cfg, key):
-            self.old[key] = getattr(self.request.cfg, key)
-        else:
-            self.new.append(key)
-        setattr(self.request.cfg, key, value)
-
-    def restore(self):
-        """ Restore previous request.cfg
-
-        Set old keys to old values and delete new keys.
-        """
-        for key, value in self.old.items():
-            setattr(self.request.cfg, key, value)
-        for key in self.new:
-            delattr(self.request.cfg, key)
-    __del__ = restore # XXX __del__ semantics are currently broken
-
-
-
 # py.test customization starts here
 
 class MoinTestFunction(py.test.collect.Function):
@@ -183,8 +96,10 @@
 
     def setup(self):
         cls = self.obj
-        cls.request = self.parent.request
-        cls.TestConfig = TestConfig(cls.request)
+        if hasattr(cls, 'Config'):
+            cls.request = init_test_request(given_config=cls.Config)
+        else:
+            cls.request = self.parent.request
         super(MoinClassCollector, self).setup()
 
 
@@ -193,7 +108,7 @@
     Function = MoinTestFunction
 
     def __init__(self, *args, **kwargs):
-        self.request = init_test_request()
+        self.request = init_test_request(given_config=wikiconfig.Config)
         super(Module, self).__init__(*args, **kwargs)
 
     def run(self, *args, **kwargs):
--- a/MoinMoin/converter/_tests/test_text_html_text_moin_wiki.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/converter/_tests/test_text_html_text_moin_wiki.py	Mon Apr 06 12:28:51 2009 +0200
@@ -23,12 +23,6 @@
 
 class TestBase(object):
 
-    def setup_method(self, method):
-        self.cfg = self.TestConfig(bang_meta=True)
-
-    def teardown_method(self, method):
-        del self.cfg
-
     def do_convert_real(self, func_args, successful=True):
         try:
             ret = convert(*func_args)
@@ -788,6 +782,67 @@
 """
         self.do(test, output)
 
+    def testPreSuccess10(self):
+        test = ur"""
+ * {{{{
+{{{
+test
+}}}
+}}}}
+
+"""
+        output = ur"""
+<ul>
+<li>
+<pre>
+{{{
+test
+}}}
+</pre>
+</li>
+</ul>
+"""
+
+    def testPreSuccess11(self):
+        test = ur"""
+ * {{{{
+test
+}}}
+}}}}
+
+"""
+        output = ur"""
+<ul>
+<li>
+<pre>
+test
+}}}
+</pre>
+</li>
+</ul>
+"""
+
+    def testPreSuccess12(self):
+        test = ur"""
+ * {{{{
+{{{
+test
+}}}}
+
+"""
+        output = ur"""
+<ul>
+<li>
+<pre>
+{{{
+test
+</pre>
+</li>
+</ul>
+"""
+
+        self.do(test, output)
+
     def testRule1(self):
         py.test.skip('broken test')
         test = ur"""
--- a/MoinMoin/converter/text_html_text_moin_wiki.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/converter/text_html_text_moin_wiki.py	Mon Apr 06 12:28:51 2009 +0200
@@ -13,6 +13,9 @@
 from MoinMoin import config, wikiutil
 from MoinMoin.error import ConvertError
 
+from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
+interwiki_re = re.compile(WikiParser.interwiki_rule, re.VERBOSE|re.UNICODE)
+
 # Portions (C) International Organization for Standardization 1986
 # Permission to copy in any form is granted for use with
 # conforming SGML systems and applications as defined in
@@ -571,7 +574,7 @@
     def process_heading(self, node):
         text = self.node_list_text_only(node.childNodes).strip()
         if text:
-            depth = int(node.localName[1]) - 1
+            depth = int(node.localName[1])
             hstr = "=" * depth
             self.text.append(self.new_line)
             self.text.append("%s %s %s" % (hstr, text.replace("\n", " "), hstr))
@@ -606,6 +609,11 @@
             style = listitem.getAttribute("style")
             if re.match(ur"list-style-type:\s*none", style, re.I):
                 markup = ". "
+                # set markup with white space when list element containes table
+                for i in listitem.childNodes:
+                    if i.nodeType == Node.ELEMENT_NODE:
+                        if i.localName == 'table':
+                            markup = ""
             else:
                 markup = "* "
         elif name == 'dl':
@@ -661,7 +669,10 @@
             self.text.append(indent)
         for i in nodelist:
             if i.nodeType == Node.ELEMENT_NODE:
-                self.process_inline(i)
+                if i.localName == 'br':
+                    self.text.append('<<BR>>')
+                else:
+                    self.process_inline(i)
             elif i.nodeType == Node.TEXT_NODE:
                 self.text.append(i.data.strip('\n').replace('\n', ' '))
         self.text.append(self.new_line)
@@ -700,6 +711,8 @@
                     self.text.append(indent)
                 self.process_table(i)
                 found = True
+            elif name == 'br':
+                pending.append(i)
             else:
                 pending.append(i)
 
@@ -792,7 +805,8 @@
             command = ",,"
         elif name == 'sup':
             command = "^"
-        elif name in ('font', 'meta', ):
+        elif name in ('area', 'center', 'code', 'embed', 'fieldset', 'font', 'form', 'iframe', 'input', 'label', 'link', 'map',
+                      'meta', 'noscript', 'option', 'script', 'select', 'textarea', 'wbr'):
             command = "" # just throw away unsupported elements
         else:
             raise ConvertError("process_inline: Don't support %s element" % name)
@@ -815,16 +829,48 @@
         self.text.append(command)
 
     def process_span(self, node):
-        # ignore span tags - just descend
+        # process span tag for firefox3
+        node_style = node.getAttribute("style")
+
         is_strike = node.getAttribute("class") == "strike"
+        is_strike = is_strike or "line-through" in node_style
+        is_strong = "bold" in node_style
+        is_italic = "italic" in node_style
+        is_underline = "underline" in node_style
+        is_comment = node.getAttribute("class") == "comment"
+
+        # start tag
+        if is_comment:
+            self.text.append("/* ")
         if is_strike:
             self.text.append("--(")
+        if is_strong:
+            self.text.append("'''")
+        if is_italic:
+            self.text.append("''")
+        if is_underline:
+            self.text.append("__")
+
+        # body
         for i in node.childNodes:
             self.process_inline(i)
+
+        # end tag
+        if is_underline:
+            self.text.append("__")
+        if is_italic:
+            self.text.append("''")
+        if is_strong:
+            self.text.append("'''")
         if is_strike:
             self.text.append(")--")
+        if is_comment:
+            self.text.append(" */")
 
     def process_div(self, node):
+        # process indent
+        self._process_indent(node)
+
         # ignore div tags - just descend
         for i in node.childNodes:
             self.visit(i)
@@ -848,9 +894,21 @@
         self.text.extend([self.new_line, "-" * length, self.new_line])
 
     def process_p(self, node):
+        # process indent
+        self._process_indent(node)
         self.process_paragraph_item(node)
         self.text.append("\n\n") # do not use self.new_line here!
 
+    def _process_indent(self, node):
+        # process indent
+        node_style = node.getAttribute("style")
+        match = re.match(r"margin-left:\s*(\d+)px", node_style)
+        if match:
+            left_margin = int(match.group(1))
+            indent_depth = int(left_margin / 40)
+            if indent_depth > 0:
+                self.text.append(' . ')
+
     def process_paragraph_item(self, node):
         for i in node.childNodes:
             if i.nodeType == Node.ELEMENT_NODE:
@@ -870,25 +928,59 @@
         if class_ == "comment": # we currently use this for stuff like ## or #acl
             for i in node.childNodes:
                 if i.nodeType == Node.TEXT_NODE:
-                    self.text.append(i.data)
-                    #print "'%s'" % i.data
+                    self.text.append(i.data.replace('\n', ''))
                 elif i.localName == 'br':
                     self.text.append(self.new_line)
                 else:
                     pass
-                    #print i.localName
         else:
-            self.text.extend(["{{{", self.new_line])
+            content_buffer = []
+            longest_inner_formater = ''
+            bang_args = ''
+            delimiters = []
+
+            """
+            below code fixed for MoinMoinBugs/GuiEditorCantNest bug
+            this has problem when outer delimiter has two more { than inside one
+            e.g. {{{{{{ {{{ foo }}} }}}}}}  --> {{{{ {{{ foo }}} }}}}
+                   {{{foo {{{ }}} foo}}} --> {{{{ {{{ }}} }}}}
+            """
+
             for i in node.childNodes:
                 if i.nodeType == Node.TEXT_NODE:
-                    self.text.append(i.data)
-                    #print "'%s'" % i.data
+                    # get longest pre tag({{{ or }}}) from content
+                    delimiters.extend(re.compile("((?u){+)").findall(i.data))
+                    delimiters.extend(re.compile("((?u)}+)").findall(i.data))
+                    # when first line is empty, start iteration second line of i.data
+                    data_lines = i.data.rstrip().split('\n')
+                    if data_lines[0].strip() == '':
+                        data_lines = data_lines[1:]
+                    for line in data_lines:
+                        if line.strip().startswith('#!'):
+                            if bang_args == '':
+                                bang_args = line.strip()
+                            else:
+                                content_buffer.extend([line, self.new_line])
+                        else:
+                            content_buffer.extend([line, self.new_line])
                 elif i.localName == 'br':
-                    self.text.append(self.new_line_dont_remove)
+                    content_buffer.append(self.new_line_dont_remove)
                 else:
                     pass
-                    #print i.localName
-            self.text.extend(["}}}", self.new_line])
+
+            if delimiters:
+                longest_inner_formater = max(delimiters)
+
+            if (len(longest_inner_formater) >= 3):
+                self.text.extend([("{" * (len(longest_inner_formater) + 1)) + bang_args, \
+                                      self.new_line])
+                self.text.extend(content_buffer)
+                self.text.extend(["}" * (len(longest_inner_formater) + 1), \
+                                      self.new_line])
+            else:
+                self.text.extend(["{{{"+bang_args, self.new_line])
+                self.text.extend(content_buffer)
+                self.text.extend(["}}}", self.new_line])
 
     _alignment = {"left": "(",
                   "center": ":",
@@ -903,19 +995,26 @@
         except ValueError:
             return value
 
-    def _table_style(self, node):
-        # TODO: attrs = get_attrs(node)
-        result = []
+    def _get_color(self, node, prefix):
         if node.hasAttribute("bgcolor"):
             value = node.getAttribute("bgcolor")
             match = re.match(r"rgb\((\d+),\s*(\d+),\s*(\d+)\)", value)
             if match:
-                result.append('tablebgcolor="#%X%X%X"' %
-                              (int(match.group(1)),
-                               int(match.group(2)),
-                               int(match.group(3))))
+                value = '#%X%X%X' % (int(match.group(1)), int(match.group(2)), int(match.group(3)))
             else:
-                result.append('tablebgcolor="%s"' % value)
+                match = re.match(r"#[0-9A-Fa-f]{6}", value)
+            if not prefix and match:
+                result = value
+            else:
+                result = '%sbgcolor="%s"' % (prefix, value)
+        else:
+            result = ''
+        return result
+
+    def _table_style(self, node):
+        # TODO: attrs = get_attrs(node)
+        result = []
+        result.append(self._get_color(node, 'table'))
         if node.hasAttribute("width"):
             value = node.getAttribute("width")
             result.append('tablewidth="%s"' % self._check_length(value))
@@ -934,16 +1033,7 @@
     def _row_style(self, node):
         # TODO: attrs = get_attrs(node)
         result = []
-        if node.hasAttribute("bgcolor"):
-            value = node.getAttribute("bgcolor")
-            match = re.match(r"rgb\((\d+),\s*(\d+),\s*(\d+)\)", value)
-            if match:
-                result.append('rowbgcolor="#%X%X%X"' %
-                              (int(match.group(1)),
-                               int(match.group(2)),
-                               int(match.group(3))))
-            else:
-                result.append('rowbgcolor="%s"' % value)
+        result.append(self._get_color(node, 'row'))
         if node.hasAttribute("style"):
             result.append('rowstyle="%s"' % node.getAttribute("style"))
         if node.hasAttribute("class"):
@@ -966,15 +1056,7 @@
 
         align = ""
         result = []
-        if  node.hasAttribute("bgcolor"):
-            value = node.getAttribute("bgcolor")
-            match = re.match(r"rgb\((\d+),\s*(\d+),\s*(\d+)\)", value)
-            if match:
-                result.append("#%X%X%X" % (int(match.group(1)),
-                                           int(match.group(2)),
-                                           int(match.group(3))))
-            else:
-                result.append('bgcolor="%s"' % value)
+        result.append(self._get_color(node, ''))
         if node.hasAttribute("align"):
             value = node.getAttribute("align")
             if not spanning or value != "center":
@@ -1007,7 +1089,8 @@
         return " ".join(result).strip()
 
     def process_table(self, node, style=""):
-        self.text.append(self.new_line)
+        if self.depth == 0:
+            self.text.append(self.new_line)
         self.new_table = True
         style += self._table_style(node)
         for i in node.childNodes:
@@ -1055,7 +1138,9 @@
             colspan = 1
         text = self.node_list_text_only(node.childNodes).replace('\n', ' ').strip()
         if text:
-            self.text.extend(["%s'''%s%s'''||" % ('||' * colspan, style, text), self.new_line_dont_remove])
+            if style:
+                style = '<%s>' % style
+            self.text.extend(["%s%s'''%s'''||" % ('||' * colspan, style, text), self.new_line_dont_remove])
 
     def process_table_data(self, node, style=""):
         if node.hasAttribute("colspan"):
@@ -1106,7 +1191,7 @@
         for i in node.childNodes:
             if i.nodeType == Node.ELEMENT_NODE:
                 name = i.localName
-                if name == 'td':
+                if name in ('td', 'th', ):
                     self.process_table_data(i, style=style)
                     style = ""
                 else:
@@ -1147,10 +1232,11 @@
                 pagename = wikiutil.url_unquote(href)
                 interwikiname = "%s:%s" % (title, pagename)
             if interwikiname and pagename == desc:
-                if ' ' in interwikiname:
+                if interwiki_re.match(interwikiname+' '): # the blank is needed by interwiki_re to match
+                    # this is valid as a free interwiki link
+                    self.text.append("%s" % interwikiname)
+                else:
                     self.text.append("[[%s]]" % interwikiname)
-                else:
-                    self.text.append("%s" % interwikiname)
                 return
             elif title == 'Self':
                 self.text.append('[[%s|%s]]' % (href, desc))
@@ -1168,10 +1254,17 @@
             # Attachments
             if title.startswith("attachment:"):
                 attname = wikiutil.url_unquote(title[len("attachment:"):])
+                if 'do=get' in href: # quick&dirty fix for not dropping &do=get param
+                    parms = '|&do=get'
+                else:
+                    parms = ''
                 if attname != desc:
-                    self.text.append('[[attachment:%s|%s]]' % (attname, desc))
+                    desc = '|%s' % desc
+                elif parms:
+                    desc = '|'
                 else:
-                    self.text.append('[[attachment:%s]]' % (attname, ))
+                    desc = ''
+                self.text.append('[[attachment:%s%s%s]]' % (attname, desc, parms))
             # wiki link
             elif href.startswith(scriptname):
                 pagename = href[len(scriptname):]
@@ -1187,6 +1280,9 @@
                 # relative link ../
                 elif desc.startswith('../') and href.endswith(desc[3:]):
                     self.text.append(wikiutil.pagelinkmarkup(desc))
+                # internal link #internal
+                elif '#' in href and pagename.startswith(self.pagename):
+                    self.text.append(wikiutil.pagelinkmarkup(href[href.index('#'):], desc))
                 # labeled link
                 else:
                     self.text.append(wikiutil.pagelinkmarkup(pagename, desc))
@@ -1287,7 +1383,16 @@
     """ get the attributes of <node> into an easy-to-use dict """
     attrs = {}
     for attr_name in node.attributes.keys():
-        attrs[attr_name] = node.attributes.get(attr_name).nodeValue
+        # get attributes of style element
+        if attr_name == "style":
+            for style_element in node.attributes.get(attr_name).nodeValue.split(';'):
+                if style_element.strip() != '':
+                    style_elements = style_element.split(':')
+                    if len(style_elements) == 2:
+                        attrs[style_elements[0].strip()] = style_elements[1].strip()
+        # get attributes without style element
+        else:
+            attrs[attr_name] = node.attributes.get(attr_name).nodeValue
     return attrs
 
 
--- a/MoinMoin/events/_tests/test_events.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/events/_tests/test_events.py	Mon Apr 06 12:28:51 2009 +0200
@@ -59,7 +59,7 @@
 
     event = events.UserCreatedEvent(request, User(request))
     request.cfg.notification_server = server_dummy()
-    request.cfg.secret = "dummy"
+    request.cfg.secrets = "thisisnotsecret"
 
     jabbernotify.handle_user_created(event)
     assert request.cfg.notification_server.sent is True
--- a/MoinMoin/events/jabbernotify.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/events/jabbernotify.py	Mon Apr 06 12:28:51 2009 +0200
@@ -50,11 +50,12 @@
 
     request = event.request
     server = request.cfg.notification_server
+    secret = request.cfg.secrets['jabberbot']
     try:
         if isinstance(event, ev.JabberIDSetEvent):
-            server.addJIDToRoster(request.cfg.secret, event.jid)
+            server.addJIDToRoster(secret, event.jid)
         else:
-            server.removeJIDFromRoster(request.cfg.secret, event.jid)
+            server.removeJIDFromRoster(secret, event.jid)
     except xmlrpclib.Error, err:
         logging.error("XML RPC error: %s" % str(err))
     except Exception, err:
@@ -197,7 +198,7 @@
         raise ValueError("url_list must be of type list!")
 
     try:
-        server.send_notification(request.cfg.secret, jids, notification)
+        server.send_notification(request.cfg.secrets['jabberbot'], jids, notification)
         return True
     except xmlrpclib.Error, err:
         logging.error("XML RPC error: %s" % str(err))
--- a/MoinMoin/events/notification.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/events/notification.py	Mon Apr 06 12:28:51 2009 +0200
@@ -52,8 +52,7 @@
     @param querystr: a dict passed to wikiutil.makeQueryString
 
     """
-    query = wikiutil.makeQueryString(querystr, True)
-    return request.getQualifiedURL(page.url(request, query))
+    return request.getQualifiedURL(page.url(request, querystr))
 
 def page_change_message(msgtype, request, page, lang, **kwargs):
     """Prepare a notification text for a page change of given type
@@ -128,12 +127,13 @@
     @return: a dict containing message body and subject
     """
     subject = _("New user account created on %(sitename)s") % {'sitename': sitename or "Wiki"}
-    text = _("""Dear Superuser, a new user has just been created. Details follow:
+    text = _("""Dear Superuser, a new user has just been created on %(sitename)s". Details follow:
 
     User name: %(username)s
     Email address: %(useremail)s""") % {
          'username': username,
          'useremail': email,
+         'sitename': sitename or "Wiki",
          }
 
     return {'subject': subject, 'text': text}
--- a/MoinMoin/events/xapian_index.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/events/xapian_index.py	Mon Apr 06 12:28:51 2009 +0200
@@ -61,7 +61,7 @@
         from MoinMoin.search.Xapian import Index
         index = Index(request)
         if index.exists():
-            index.update_page(request.page.page_name)
+            index.update_page(event.pagename)
 
 
 def handle(event):
--- a/MoinMoin/failure.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/failure.py	Mon Apr 06 12:28:51 2009 +0200
@@ -7,13 +7,13 @@
     @license: GNU GPL, see COPYING for details.
 """
 import sys, os
+import traceback
 
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
 from MoinMoin.support import cgitb
 from MoinMoin.error import ConfigurationError
-from traceback import extract_tb
 
 
 class View(cgitb.View):
@@ -69,7 +69,7 @@
         text = [self.formatExceptionMessage(self.info)]
 
         if self.info[0] == ConfigurationError:
-            tbt = extract_tb(self.info[1].exceptions()[-1][2])[-1]
+            tbt = traceback.extract_tb(self.info[1].exceptions()[-1][2])[-1]
             text.append(
                 f.paragraph('Error in your configuration file "%s"'
                             ' around line %d.' % tbt[:2]))
@@ -156,33 +156,44 @@
         raise err
 
     savedError = sys.exc_info()
-    logging.exception('An exception occured.')
+    logging.exception('An exception occurred, URI was "%s".' % request.request_uri)
+
     try:
-        debug = 'debug' in getattr(request, 'form', {})
+        display = request.cfg.traceback_show # might fail if we have no cfg yet
+    except:
         # default to True here to allow an admin setting up the wiki
         # to see the errors made in the configuration file
         display = True
-        logdir = None
-        if hasattr(request, 'cfg'):
-            display = request.cfg.traceback_show
-            logdir = request.cfg.traceback_log_dir
-        handler = cgitb.Hook(file=request, display=display, logdir=logdir,
-                             viewClass=View, debug=debug)
-        handler.handle()
+
+    try:
+        debug = 'debug' in request.form
     except:
+        debug = False
+
+    try:
+        # try to output a nice html error page
+        handler = cgitb.Hook(file=request, display=display, viewClass=View, debug=debug)
+        handler.handle(savedError)
+    except:
+        # if that fails, log the cgitb problem ...
+        logging.exception('cgitb raised this exception')
+        # ... and try again with a simpler output method:
         request.write('<pre>\n')
-        printTextException(request, savedError)
+        printTextException(request, savedError, display)
         request.write('\nAdditionally cgitb raised this exception:\n')
-        printTextException(request)
+        printTextException(request, display=display)
         request.write('</pre>\n')
 
 
-def printTextException(request, info=None):
+
+def printTextException(request, info=None, display=True):
     """ Simple text exception that should never fail
 
     Print all exceptions in a composite error.
     """
-    import traceback
+    if not display:
+        request.write("(Traceback display forbidden by configuration)\n")
+        return
     from MoinMoin import wikiutil
     if info is None:
         info = sys.exc_info()
--- a/MoinMoin/formatter/__init__.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/formatter/__init__.py	Mon Apr 06 12:28:51 2009 +0200
@@ -14,7 +14,6 @@
 
 from MoinMoin.util import pysupport
 from MoinMoin import wikiutil
-from MoinMoin.support.python_compatibility import rsplit
 
 modules = pysupport.getPackageModules(__file__)
 
@@ -106,9 +105,6 @@
         """
         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_interwiki(self.request, interwiki, pagename)
         if wikitag == 'Self' or wikitag == self.request.cfg.interwikiname:
-            if '#' in wikitail:
-                wikitail, kw['anchor'] = rsplit(wikitail, '#', 1)
-                wikitail = wikiutil.url_unquote(wikitail)
             return self.pagelink(on, wikitail, **kw)
         return ''
 
--- a/MoinMoin/formatter/_tests/test_formatter.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/formatter/_tests/test_formatter.py	Mon Apr 06 12:28:51 2009 +0200
@@ -14,6 +14,7 @@
 
 class TestFormatter:
     def testSyntaxReferenceDomXml(self):
+        py.test.skip("domxml <p> generation is broken")
         f_name = 'dom_xml'
         try:
             formatter = wikiutil.importPlugin(self.request.cfg, "formatter", f_name, "Formatter")
@@ -21,7 +22,7 @@
             pass
         else:
             print "Formatting using %r" % formatter
-            self.formatPage("SyntaxReference", formatter)
+            self.formatPage("HelpOnMoinWikiSyntax", formatter)
             print "Done."
 
     def testSyntaxReferenceDocBook(self):
@@ -57,7 +58,7 @@
                 pass
             else:
                 print "Formatting using %r" % formatter
-                self.formatPage("SyntaxReference", formatter)
+                self.formatPage("HelpOnMoinWikiSyntax", formatter)
                 print "Done."
 
     def formatPage(self, pagename, formatter):
@@ -68,6 +69,7 @@
         self.request.reset()
         page = Page(self.request, pagename, formatter=formatter)
         self.request.formatter = page.formatter = formatter(self.request)
+        self.request.page = page
         #page.formatter.setPage(page)
         #page.hilite_re = None
 
--- a/MoinMoin/formatter/text_gedit.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/formatter/text_gedit.py	Mon Apr 06 12:28:51 2009 +0200
@@ -25,7 +25,7 @@
             self._base_depth = depth
 
         count_depth = max(depth - (self._base_depth - 1), 1)
-        heading_depth = depth + 1
+        heading_depth = depth
 
         # closing tag, with empty line after, to make source more readable
         if not on:
@@ -68,10 +68,15 @@
             return '<span style="background-color:#ffff11">{{attachment:%s|%s}}</span>' % (url, text)
 
     def attachment_link(self, on, url=None, **kw):
+        assert on in (0, 1, False, True) # make sure we get called the new way, not like the 1.5 api was
         _ = self.request.getText
+        querystr = kw.get('querystr', {})
+        assert isinstance(querystr, dict) # new in 1.6, only support dicts
+        if 'do' not in querystr:
+            querystr['do'] = 'view'
         if on:
             pagename = self.page.page_name
-            target = AttachFile.getAttachUrl(pagename, url, self.request)
+            target = AttachFile.getAttachUrl(pagename, url, self.request, do=querystr['do'])
             return self.url(on, target, title="attachment:%s" % wikiutil.quoteWikinameURL(url))
         else:
             return self.url(on)
@@ -234,3 +239,9 @@
     def line_anchorlink(self, on, lineno=0):
         return '' # not needed for gui editor feeding
 
+    def span(self, on, **kw):
+        previous_state = self.request.user.show_comments
+        self.request.user.show_comments = True
+        ret = text_html.Formatter.span(self, on, **kw)
+        self.request.user.show_comments = previous_state
+        return ret
--- a/MoinMoin/formatter/text_html.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/formatter/text_html.py	Mon Apr 06 12:28:51 2009 +0200
@@ -14,7 +14,7 @@
 from MoinMoin import wikiutil, i18n
 from MoinMoin.Page import Page
 from MoinMoin.action import AttachFile
-from MoinMoin.support.python_compatibility import set, rsplit
+from MoinMoin.support.python_compatibility import set
 
 # insert IDs into output wherever they occur
 # warning: breaks toggle line numbers javascript
@@ -488,8 +488,6 @@
         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_interwiki(self.request, interwiki, pagename)
         wikiurl = wikiutil.mapURL(self.request, wikiurl)
         if wikitag == 'Self': # for own wiki, do simple links
-            if '#' in wikitail:
-                wikitail, kw['anchor'] = rsplit(wikitail, '#', 1)
             wikitail = wikiutil.url_unquote(wikitail)
             try: # XXX this is the only place where we access self.page - do we need it? Crashes silently on actions!
                 pagename = wikiutil.AbsPageName(self.page.page_name, wikitail)
@@ -502,6 +500,9 @@
                 if querystr:
                     separator = ('?', '&')['?' in href]
                     href = '%s%s%s' % (href, separator, wikiutil.makeQueryString(querystr))
+                anchor = kw.get('anchor')
+                if anchor:
+                    href = '%s#%s' % (href, self.sanitize_to_id(anchor))
                 if wikitag_bad:
                     html_class = 'badinterwiki'
                 else:
@@ -652,6 +653,7 @@
             return self.url(1, target, css=css, title=title) + img + self.url(0)
 
     def attachment_drawing(self, url, text, **kw):
+        # XXX text arg is unused!
         _ = self.request.getText
         pagename, filename = AttachFile.absoluteName(url, self.page.page_name)
         fname = wikiutil.taintfilename(filename)
@@ -670,42 +672,42 @@
         # check whether attachment exists, possibly point to upload form
         drawing_url = AttachFile.getAttachUrl(pagename, fname, self.request, drawing=drawing, upload=True)
         if not exists:
-            linktext = _('Create new drawing "%(filename)s (opens in new window)"')
-            return (self.url(1, drawing_url) +
-                    self.text(linktext % {'filename': fname}) +
-                    self.url(0))
+            title = _('Create new drawing "%(filename)s (opens in new window)"') % {'filename': fname}
+            img = self.icon('attachimg')  # TODO: we need a new "drawimg" in similar grey style and size
+            css = 'nonexistent'
+            return self.url(1, drawing_url, css=css, title=title) + img + self.url(0)
+
+        title = _('Edit drawing %(filename)s (opens in new window)') % {'filename': self.text(fname)}
+        kw['src'] = AttachFile.getAttachUrl(pagename, filename, self.request, addts=1)
+        kw['css'] = 'drawing'
 
         mappath = AttachFile.getFilename(self.request, pagename, drawing + u'.map')
-
-        # check for map file
-        if os.path.exists(mappath):
+        try:
+            map = file(mappath, 'r').read()
+        except (IOError, OSError):
+            map = ''
+        if map:
             # we have a image map. inline it and add a map ref to the img tag
-            try:
-                map = file(mappath, 'r').read()
-            except IOError:
-                pass
-            except OSError:
-                pass
-            else:
-                mapid = 'ImageMapOf' + drawing
-                # replace MAPNAME
-                map = map.replace('%MAPNAME%', mapid)
-                # add alt and title tags to areas
-                map = re.sub('href\s*=\s*"((?!%TWIKIDRAW%).+?)"', r'href="\1" alt="\1" title="\1"', map)
-                # add in edit links plus alt and title attributes
-                alt = title = _('Edit drawing %(filename)s (opens in new window)') % {'filename': self.text(fname)}
-                map = map.replace('%TWIKIDRAW%"', '%s" alt="%s" title="%s"' % (drawing_url, alt, title))
-                # unxml, because 4.01 concrete will not validate />
-                map = map.replace('/>', '>')
-                alt = title = _('Clickable drawing: %(filename)s') % {'filename': self.text(fname)}
-                src = AttachFile.getAttachUrl(pagename, filename, self.request, addts=1)
-                return (map + self.image(alt=alt, title=title, src=src, usemap='#'+mapid, css="drawing"))
+            mapid = 'ImageMapOf' + drawing
+            map = map.replace('%MAPNAME%', mapid)
+            # add alt and title tags to areas
+            map = re.sub('href\s*=\s*"((?!%TWIKIDRAW%).+?)"', r'href="\1" alt="\1" title="\1"', map)
+            map = map.replace('%TWIKIDRAW%"', '%s" alt="%s" title="%s"' % (drawing_url, title, title))
+            # unxml, because 4.01 concrete will not validate />
+            map = map.replace('/>', '>')
+            title = _('Clickable drawing: %(filename)s') % {'filename': self.text(fname)}
+            if 'title' not in kw:
+                kw['title'] = title
+            if 'alt' not in kw:
+                kw['alt'] = kw['title']
+            kw['usemap'] = '#'+mapid
+            return map + self.image(**kw)
         else:
-            alt = title = _('Edit drawing %(filename)s (opens in new window)') % {'filename': self.text(fname)}
-            src = AttachFile.getAttachUrl(pagename, filename, self.request, addts=1)
-            return (self.url(1, drawing_url) +
-                    self.image(alt=alt, title=title, src=src, css="drawing") +
-                    self.url(0))
+            if 'title' not in kw:
+                kw['title'] = title
+            if 'alt' not in kw:
+                kw['alt'] = kw['title']
+            return self.url(1, drawing_url) + self.image(**kw) + self.url(0)
 
 
     # Text ##############################################################
--- a/MoinMoin/formatter/text_html_percent.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/formatter/text_html_percent.py	Mon Apr 06 12:28:51 2009 +0200
@@ -17,7 +17,7 @@
 class Formatter(TextHtmlFormatter):
 
     def _open(self, tag, newline=False, attr=None, allowed_attrs=None, **kw):
-        """ Escape % signs in tags, see also text_html.Formatter._open. """
+        """ Escape % characters in tags, see also text_html.Formatter._open. """
         tagstr = TextHtmlFormatter._open(self, tag, newline, attr, allowed_attrs, **kw)
         return tagstr.replace('%', '%%')
 
--- a/MoinMoin/i18n/MoinMoin.pot	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/i18n/MoinMoin.pot	Mon Apr 06 12:28:51 2009 +0200
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-09-07 23:23+0200\n"
+"POT-Creation-Date: 2009-02-07 14:05+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -473,6 +473,9 @@
 msgid "about"
 msgstr ""
 
+msgid "anonymous"
+msgstr ""
+
 msgid "Language"
 msgstr ""
 
@@ -697,7 +700,8 @@
 
 #, python-format
 msgid ""
-"Dear Superuser, a new user has just been created. Details follow:\n"
+"Dear Superuser, a new user has just been created on %(sitename)s\". Details "
+"follow:\n"
 "\n"
 "    User name: %(username)s\n"
 "    Email address: %(useremail)s"
@@ -731,6 +735,24 @@
 msgid "Page changed"
 msgstr ""
 
+msgid ""
+" Emphasis:: <<Verbatim('')>>''italics''<<Verbatim('')>>; <<Verbatim(''')"
+">>'''bold'''<<Verbatim(''')>>; <<Verbatim(''''')>>'''''bold "
+"italics'''''<<Verbatim(''''')>>; <<Verbatim('')>>''mixed ''<<Verbatim(''')"
+">>'''''bold'''<<Verbatim(''')>> and italics''<<Verbatim('')>>; <<Verbatim"
+"(----)>> horizontal rule.\n"
+" Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; "
+"===== Title 5 =====.\n"
+" Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1."
+"#n start numbering at n; space alone indents.\n"
+" Links:: <<Verbatim(JoinCapitalizedWords)>>; <<Verbatim([[target|linktext]])"
+">>.\n"
+" Tables:: || cell text |||| cell text spanning 2 columns ||;    no trailing "
+"white space allowed after tables or titles.\n"
+"\n"
+"(!) For more help, see HelpOnEditing or SyntaxReference.\n"
+msgstr ""
+
 #, python-format
 msgid "Expected \"%(wanted)s\" after \"%(key)s\", got \"%(token)s\""
 msgstr ""
@@ -761,6 +783,26 @@
 "Rendering of reStructured text is not possible, please install Docutils."
 msgstr ""
 
+msgid ""
+"{{{\n"
+"Emphasis: *italic* **bold** ``monospace``\n"
+"\n"
+"Headings: Heading 1  Heading 2  Heading 3\n"
+"          =========  ---------  ~~~~~~~~~\n"
+"\n"
+"Horizontal rule: ----\n"
+"\n"
+"Links: TrailingUnderscore_ `multi word with backticks`_ external_\n"
+"\n"
+".. _external: http://external-site.example.org/foo/\n"
+"\n"
+"Lists: * bullets; 1., a. numbered items.\n"
+"}}}\n"
+"(!) For more help, see the\n"
+"[[http://docutils.sourceforge.net/docs/user/rst/quickref.html|"
+"reStructuredText Quick Reference]].\n"
+msgstr ""
+
 msgid "**Maximum number of allowed includes exceeded**"
 msgstr ""
 
@@ -772,6 +814,23 @@
 msgid "**Could not find the referenced page: %s**"
 msgstr ""
 
+msgid ""
+" Emphasis:: <<Verbatim(//)>>''italics''<<Verbatim(//)>>; <<Verbatim(**)"
+">>'''bold'''<<Verbatim(**)>>; <<Verbatim(**//)>>'''''bold "
+"italics'''''<<Verbatim(//**)>>; <<Verbatim(//)>>''mixed ''<<Verbatim(**)"
+">>'''''bold'''<<Verbatim(**)>> and italics''<<Verbatim(//)>>;\n"
+" Horizontal Rule:: <<Verbatim(----)>>\n"
+" Force Linebreak:: <<Verbatim(\\\\)>>\n"
+" Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; "
+"===== Title 5 =====.\n"
+" Lists:: * bullets; ** sub-bullets; # numbered items; ## numbered sub "
+"items.\n"
+" Links:: <<Verbatim([[target]])>>; <<Verbatim([[target|linktext]])>>.\n"
+" Tables:: |= header text | cell text | more cell text |;\n"
+"\n"
+"(!) For more help, see HelpOnEditing or HelpOnCreoleSyntax.\n"
+msgstr ""
+
 msgid "Preferences"
 msgstr ""
 
@@ -1162,43 +1221,27 @@
 "username, the recovery token and a new password (twice) below."
 msgstr ""
 
-#, python-format
-msgid ""
-"Restored Backup: %(filename)s to target dir: %(targetdir)s.\n"
-"Files: %(filecount)d, Directories: %(dircount)d"
-msgstr ""
-
-#, python-format
-msgid "Restoring backup: %(filename)s to target dir: %(targetdir)s failed."
-msgstr ""
-
-msgid "Wiki Backup / Restore"
+msgid "Wiki Backup"
 msgstr ""
 
 msgid ""
-"Some hints:\n"
-" * To restore a backup:\n"
-"  * Restoring a backup will overwrite existing data, so be careful.\n"
-"  * Rename it to <siteid>.tar.<compression> (remove the --date--time--UTC "
-"stuff).\n"
-"  * Put the backup file into the backup_storage_dir (use scp, ftp, ...).\n"
-"  * Hit the <<GetText(Restore)>> button below.\n"
+"= Downloading a backup =\n"
 "\n"
-" * To make a backup, just hit the <<GetText(Backup)>> button and save the "
-"file\n"
-"   you get to a secure place.\n"
+"Please note:\n"
+" * Store backups in a safe and secure place - they contain sensitive "
+"information.\n"
+" * Make sure your wiki configuration backup_* values are correct and "
+"complete.\n"
+" * Make sure the backup file you get contains everything you need in case of "
+"problems.\n"
+" * Make sure it is downloaded without problems.\n"
 "\n"
-"Please make sure your wiki configuration backup_* values are correct and "
-"complete.\n"
-"\n"
+"To get a backup, just click here:"
 msgstr ""
 
 msgid "Backup"
 msgstr ""
 
-msgid "Restore"
-msgstr ""
-
 msgid "You are not allowed to do remote backup."
 msgstr ""
 
@@ -2005,59 +2048,17 @@
 msgid "You need to be subscribed to unsubscribe."
 msgstr ""
 
-msgid ""
-" Emphasis:: <<Verbatim('')>>''italics''<<Verbatim('')>>; <<Verbatim(''')"
-">>'''bold'''<<Verbatim(''')>>; <<Verbatim(''''')>>'''''bold "
-"italics'''''<<Verbatim(''''')>>; <<Verbatim('')>>''mixed ''<<Verbatim(''')"
-">>'''''bold'''<<Verbatim(''')>> and italics''<<Verbatim('')>>; <<Verbatim"
-"(----)>> horizontal rule.\n"
-" Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; "
-"===== Title 5 =====.\n"
-" Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1."
-"#n start numbering at n; space alone indents.\n"
-" Links:: <<Verbatim(JoinCapitalizedWords)>>; <<Verbatim([[target|linktext]])"
-">>.\n"
-" Tables:: || cell text |||| cell text spanning 2 columns ||;    no trailing "
-"white space allowed after tables or titles.\n"
-"\n"
-"(!) For more help, see HelpOnEditing or SyntaxReference.\n"
+msgid "Password is too short."
+msgstr ""
+
+msgid "Password has not enough different characters."
 msgstr ""
 
 msgid ""
-"{{{\n"
-"Emphasis: *italic* **bold** ``monospace``\n"
-"\n"
-"Headings: Heading 1  Heading 2  Heading 3\n"
-"          =========  ---------  ~~~~~~~~~\n"
-"\n"
-"Horizontal rule: ----\n"
-"\n"
-"Links: TrailingUnderscore_ `multi word with backticks`_ external_\n"
-"\n"
-".. _external: http://external-site.example.org/foo/\n"
-"\n"
-"Lists: * bullets; 1., a. numbered items.\n"
-"}}}\n"
-"(!) For more help, see the\n"
-"[[http://docutils.sourceforge.net/docs/user/rst/quickref.html|"
-"reStructuredText Quick Reference]].\n"
-msgstr ""
-
-msgid ""
-" Emphasis:: <<Verbatim(//)>>''italics''<<Verbatim(//)>>; <<Verbatim(**)"
-">>'''bold'''<<Verbatim(**)>>; <<Verbatim(**//)>>'''''bold "
-"italics'''''<<Verbatim(//**)>>; <<Verbatim(//)>>''mixed ''<<Verbatim(**)"
-">>'''''bold'''<<Verbatim(**)>> and italics''<<Verbatim(//)>>;\n"
-" Horizontal Rule:: <<Verbatim(----)>>\n"
-" Force Linebreak:: <<Verbatim(\\\\)>>\n"
-" Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; "
-"===== Title 5 =====.\n"
-" Lists:: * bullets; ** sub-bullets; # numbered items; ## numbered sub "
-"items.\n"
-" Links:: <<Verbatim([[target]])>>; <<Verbatim([[target|linktext]])>>.\n"
-" Tables:: |= header text | cell text | more cell text |;\n"
-"\n"
-"(!) For more help, see HelpOnEditing or HelpOnCreoleSyntax.\n"
+"Password is too easy (password contains name or name contains password)."
+msgstr ""
+
+msgid "Password is too easy (keyboard sequence)."
 msgstr ""
 
 msgid "Diffs"
@@ -2295,6 +2296,15 @@
 msgid "User"
 msgstr ""
 
+msgid "[ATTACH]"
+msgstr ""
+
+msgid "Variable name"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
 #, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr ""
@@ -2335,6 +2345,19 @@
 msgid "Display"
 msgstr ""
 
+msgid "Wiki configuration"
+msgstr ""
+
+msgid ""
+"This table shows all settings in this wiki that do not have default values. "
+"Settings that the configuration system doesn't know about are shown in "
+"''italic'', those may be due to third-party extensions needing configuration "
+"or settings that were removed from Moin."
+msgstr ""
+
+msgid "Setting"
+msgstr ""
+
 msgid "Search for items"
 msgstr ""
 
--- a/MoinMoin/i18n/__init__.py	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/i18n/__init__.py	Mon Apr 06 12:28:51 2009 +0200
@@ -60,7 +60,10 @@
     request.clock.start('i18n_init')
     if languages is None:
         logging.debug("trying to load translations from cache")
-        meta_cache = caching.CacheEntry(request, 'i18n', 'meta', scope='farm', use_pickle=True)
+        # the scope of the i18n cache needs to be per-wiki, because some translations
+        # have http links (to some help pages) and they must not point to another
+        # wiki in the farm (confusing and maybe not even readable due to ACLs):
+        meta_cache = caching.CacheEntry(request, 'i18n', 'meta', scope='wiki', use_pickle=True)
         i18n_dir = os.path.join(request.cfg.moinmoin_dir, 'i18n')
         if meta_cache.needsUpdate(i18n_dir):
             logging.debug("cache needs update")
@@ -199,7 +202,8 @@
 
     def loadLanguage(self, request, trans_dir="i18n"):
         request.clock.start('loadLanguage')
-        cache = caching.CacheEntry(request, arena='i18n', key=self.language, scope='farm', use_pickle=True)
+        # see comment about per-wiki scope above
+        cache = caching.CacheEntry(request, arena='i18n', key=self.language, scope='wiki', use_pickle=True)
         langfilename = po_filename(request, self.language, self.domain, i18n_dir=trans_dir)
         needsupdate = cache.needsUpdate(langfilename)
         if not needsupdate:
@@ -290,7 +294,7 @@
             # do not simply return trans with str, but recursively call
             # to get english translation, maybe formatted.
             # if we don't find an english "translation", we just format it
-            # on the fly (this is needed for cfg.editor_quickhelp).
+            # on the fly (this is needed for quickhelp).
             if lang != 'en':
                 logging.debug("falling back to english, requested string not in %r translation: %r" % (lang, original))
                 translated = getText(original, request, 'en', wiki=formatted, percent=percent)
--- a/MoinMoin/i18n/ar.MoinMoin.po	Mon Apr 06 12:27:11 2009 +0200
+++ b/MoinMoin/i18n/ar.MoinMoin.po	Mon Apr 06 12:28:51 2009 +0200
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: moin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-09-07 23:23+0200\n"
+"POT-Creation-Date: 2009-01-24 04:45+0100\n"
 "PO-Revision-Date: 2008-04-12 20:07+0200\n"
 "Last-Translator: Mohamed Yahya <yahya.mohamed@gmail.com>\n"
 "Language-Team: \n"
@@ -26,6 +26,33 @@
 "X-HasWikiMarkup: True\n"
 
 #, python-format
+msgid "Invalid highlighting regular expression \"%(regex)s\": %(error)s"
+msgstr ""
+
+msgid ""
+"The backed up content of this page is deprecated and will not be included in "
+"search results!"
+msgstr ""
+
+#, python-format
+msgid "Revision %(rev)d as of %(date)s"
+msgstr ""
+
+#, python-format
+msgid "Redirected from page \"%(page)s\""
+msgstr ""
+
+#, python-format
+msgid "This page redirects to page \"%(page)s\""
+msgstr ""
+
+msgid "Create New Page"
+msgstr "اخلق صفحة وجديدة"
+
+msgid "You are not allowed to view this page."
+msgstr "لا يسمح لك بنظر هذه الصفحة."
+
+#, python-format
 msgid "Argument \"%s\" must be a boolean value, not \"%s\""
 msgstr ""
 
@@ -87,9 +114,6 @@
 msgid "Expected a value for key \"%(token)s\""
 msgstr "تتوقع قيمة للمفتاح \"%(token)s\""
 
-msgid "Your changes are not saved!"
-msgstr "لم يتم حفظ تغييراتك!"
-
 msgid "You are not allowed to edit this page."
 msgstr "لا يسمح لك بتحرير هذه الصفحة"
 
@@ -102,9 +126,6 @@
 msgid "The lock you held timed out. Be prepared for editing conflicts!"
 msgstr ""
 
-msgid "Page name is too long, try shorter name."
-msgstr "إسم الصفحة طويل، جرب إسما أقصر."
-
 #, fuzzy, python-format
 msgid "Draft of \"%(pagename)s\""
 msgstr "تحرير \"%(pagename)s\""
@@ -188,8 +209,9 @@
 msgid "Preview"
 msgstr "معاينة"
 
-msgid "GUI Mode"
-msgstr "وضعية واجهة المستخدم الرسومية"
+#, fuzzy
+msgid "Text mode"
+msgstr "نص عادي"
 
 #, fuzzy
 msgid "Load Draft"
@@ -211,6 +233,75 @@
 msgid "Remove trailing whitespace from each line"
 msgstr "أزل الفراغ الأبيض في نهاية كل سطر."
 
+#, python-format
+msgid "The package needs a newer version of MoinMoin (at least %s)."
+msgstr ""
+
+msgid "The theme name is not set."
+msgstr ""
+
+msgid "Installing theme files is only supported for standalone type servers."
+msgstr ""
+
+#, python-format
+msgid "Installation of '%(filename)s' failed."
+msgstr ""
+
+#, python-format
+msgid "The file %s is not a MoinMoin package file."
+msgstr ""
+
+#, python-format
+msgid "The page %s does not exist."
+msgstr "الصفحة %s غير موجودة."
+
+msgid "Invalid package file header."
+msgstr ""
+
+msgid "Package file format unsupported."
+msgstr ""
+
+#, python-format
+msgid "Unknown function %(func)s in line %(lineno)i."
+msgstr ""
+
+#, python-format
+msgid "The file %s was not found in the package."
+msgstr ""
+
+msgid "<unknown>"
+msgstr "<مغمور>"
+
+#, python-format
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s/?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr ""
+
+msgid "Your changes are not saved!"
+msgstr "لم يتم حفظ تغييراتك!"
+
+msgid "Page name is too long, try shorter name."
+msgstr "إسم الصفحة طويل، جرب إسما أقصر."
+
+msgid "GUI Mode"
+msgstr "وضعية واجهة المستخدم الرسومية"
+
 #, fuzzy
 msgid "Edit was cancelled."
 msgstr "تم إلغاء التحرير."
@@ -338,36 +429,6 @@
 "To leave the editor, press the Cancel button."
 msgstr ""
 
-msgid "<unknown>"
-msgstr "<مغمور>"
-
-#, python-format
-msgid "Invalid highlighting regular expression \"%(regex)s\": %(error)s"
-msgstr ""
-
-msgid ""
-"The backed up content of this page is deprecated and will not be included in "
-"search results!"
-msgstr ""
-
-#, python-format
-msgid "Revision %(rev)d as of %(date)s"
-msgstr ""
-
-#, python-format
-msgid "Redirected from page \"%(page)s\""
-msgstr ""
-
-#, python-format
-msgid "This page redirects to page \"%(page)s\""
-msgstr ""
-
-msgid "Create New Page"
-msgstr "اخلق صفحة وجديدة"
-
-msgid "You are not allowed to view this page."
-msgstr "لا يسمح لك بنظر هذه الصفحة."
-
 msgid "The wiki is currently not reachable."
 msgstr "لا يمكن الوصول إلى هذه الويكي حاليا."
 
@@ -380,84 +441,6 @@
 "than you specified (%(localname)s)."
 msgstr ""
 
-#, fuzzy
-msgid "Text mode"
-msgstr "نص عادي"
-
-#, python-format
-msgid "The package needs a newer version of MoinMoin (at least %s)."
-msgstr ""
-
-msgid "The theme name is not set."
-msgstr ""
-
-msgid "Installing theme files is only supported for standalone type servers."
-msgstr ""
-
-#, python-format
-msgid "Installation of '%(filename)s' failed."
-msgstr ""
-
-#, python-format
-msgid "The file %s is not a MoinMoin package file."
-msgstr ""
-
-#, python-format
-msgid "The page %s does not exist."
-msgstr "الصفحة %s غير موجودة."
-
-msgid "Invalid package file header."
-msgstr ""
-
-msgid "Package file format unsupported."
-msgstr ""
-
-#, python-format
-msgid "Unknown function %(func)s in line %(lineno)i."
-msgstr ""
-
-#, python-format
-msgid "The file %s was not found in the package."
-msgstr ""
-
-#, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s/?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr ""
-
-msgid "Line"
-msgstr "سطر"
-
-msgid "No differences found!"
-msgstr "لا توجد الاختلافات!"
-
-msgid "Deletions are marked like this."
-msgstr ""
-
-msgid "Additions are marked like this."
-msgstr ""
-
-#, python-format
-msgid ""
-"Sorry, can not save page because \"%(content)s\" is not allowed in this wiki."
-msgstr ""
-
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -498,312 +481,43 @@
 msgid "about"
 msgstr ""
 
-msgid "Language"
-msgstr "لغة"
-
-msgid "<Browser setting>"
-msgstr "<إعدادات المتصفح>"
-
-msgid "Others"
-msgstr ""
-
-msgid "Date"
-msgstr "التاريخ"
-
-msgid "Views/day"
-msgstr ""
-
-msgid "Edits/day"
-msgstr ""
-
-msgid "Page hits and edits"
-msgstr ""
+msgid "[all]"
+msgstr ""
+
+msgid "[not empty]"
+msgstr ""
+
+msgid "[empty]"
+msgstr ""
+
+msgid "filter"
+msgstr "مرشح"
+
+#, fuzzy
+msgid "Change password"
+msgstr "كلمةالسرّ"
+
+msgid "Passwords don't match!"
+msgstr "كلمات السر غير متطابقة!"
+
+msgid "Please specify a password!"
+msgstr "الرجاء إدخال كلمة سر!"
 
 #, python-format
-msgid "%(chart_title)s for %(filterpage)s"
-msgstr ""
-
-msgid ""
-"green=view\n"
-"red=edit"
-msgstr ""
-
-msgid "date"
-msgstr "التاريخ"
-
-msgid "# of hits"
-msgstr ""
-
-msgid "Charts are not available!"
-msgstr ""
-
-msgid "Page Size Distribution"
-msgstr ""
-
-msgid "page size upper bound [bytes]"
-msgstr ""
-
-msgid "# of pages of this size"
-msgstr ""
-
-msgid "User agent"
-msgstr ""
-
-msgid "Distribution of User-Agent Types"
-msgstr ""
-
-msgid "Could not contact botbouncer.com."
-msgstr ""
-
-msgid "Failed to connect to database."
-msgstr "فشلت محاولة الإتصال بقاعدة البيانات."
-
-msgid "Missing password. Please enter user name and password."
-msgstr "لا يوجد كلمة سر. الرجاء إدخال إسم مستخدم و كلمة سر"
-
-#, python-format
-msgid ""
-"If you do not have an account, <a href=\"%(userprefslink)s\">you can create "
-"one now</a>. <a href=\"%(sendmypasswordlink)s\">Forgot your password?</a>"
-msgstr ""
-
-#, fuzzy
-msgid "You need to log in."
-msgstr "لقد قمت بتسجيل الخروج."
-
-#, python-format
-msgid "LDAP server %(server)s failed."
-msgstr ""
-
-msgid ""
-"Please choose an account name now.\n"
-"If you choose an existing account name you will be asked for the\n"
-"password and be able to associate the account with your OpenID."
-msgstr ""
-
-msgid "Name"
-msgstr "اسم"
-
-msgid "Choose this name"
-msgstr "إختر هذا الإسم"
-
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
-msgid ""
-"The username you have chosen is already\n"
-"taken. If it is your username, enter your password below to associate\n"
-"the username with your OpenID. Otherwise, please choose a different\n"
-"username and leave the password field blank."
-msgstr ""
+msgid "Password not acceptable: %s"
+msgstr "كلمة السر غير مقبولة: %s"
+
+msgid "Your password has been changed."
+msgstr "تم تغيير كلمة السر الخاصة بك."
+
+msgid "To change your password, enter a new password twice."
+msgstr "لتغيير كلمة السر الخاصة بك، أدخل كلمة سر جديدة مرتين."
 
 msgid "Password"
 msgstr "كلمةالسرّ"
 
-msgid "Associate this name"
-msgstr ""
-
-#, python-format
-msgid "OpenID error: %s."
-msgstr ""
-
-#, fuzzy
-msgid "Verification canceled."
-msgstr "تم إلغاء العملية."
-
-msgid "OpenID failure."
-msgstr ""
-
-msgid "Your account is now associated to your OpenID."
-msgstr ""
-
-msgid "The password you entered is not valid."
-msgstr "كلمة السر التي أدخلتها غير صالحة."
-
-msgid "OpenID verification requires that you click this button:"
-msgstr ""
-
-msgid "Anonymous sessions need to be enabled for OpenID login."
-msgstr ""
-
-msgid "Failed to resolve OpenID."
-msgstr ""
-
-msgid "OpenID discovery failure, not a valid OpenID."
-msgstr ""
-
-msgid "No OpenID."
-msgstr ""
-
-msgid ""
-"If you do not have an account yet, you can still log in with your OpenID and "
-"create one during login."
-msgstr ""
-
-msgid "Page has been modified"
-msgstr "تم تعديل الصفحة"
-
-msgid "Page has been modified in a trivial fashion"
-msgstr "تم تعديل الصفحة بشكل بسيط"
-
-msgid "Page has been renamed"
-msgstr "تم إعادة تسمية الصفحة"
-
-msgid "Page has been deleted"
-msgstr "تم حذف الصفحة"
-
-msgid "Page has been copied"
-msgstr "تم نسخ الصفحة"
-
-#, fuzzy
-msgid "A new attachment has been added"
-msgstr "المرفقات"
-
-msgid "A page has been reverted to a previous state"
-msgstr ""
-
-#, fuzzy
-msgid "A user has subscribed to a page"
-msgstr "لا يسمح لك بتحرير هذه الصفحة"
-
-msgid "A new account has been created"
-msgstr "تم إنشاء حساب جديد"
-
-#, python-format
-msgid "[%(sitename)s] %(trivial)sUpdate of \"%(pagename)s\" by %(username)s"
-msgstr ""
-
-msgid "Trivial "
-msgstr "بديهي"
-
-#, python-format
-msgid ""
-"Attachment link: %(attach)s\n"
-"Page link: %(page)s\n"
-msgstr ""
-
-#, python-format
-msgid ""
-"Dear Wiki user,\n"
-"\n"
-"You have subscribed to a wiki page or wiki category on \"%(sitename)s\" for "
-"change notification.\n"
-"\n"
-"The \"%(pagename)s\" page has been changed by %(editor)s:\n"
-msgstr ""
-
-msgid "New page:\n"
-msgstr "صفحة جديدة\n"
-
-msgid "No differences found!\n"
-msgstr "ْلا توجد اختلافات \n"
-
-#, python-format
-msgid ""
-"Dear wiki user,\n"
-"\n"
-"You have subscribed to a wiki page \"%(sitename)s\" for change "
-"notification.\n"
-"\n"
-"The page \"%(pagename)s\" has been deleted by %(editor)s:\n"
-"\n"
-msgstr ""
-
-#, python-format
-msgid ""
-"Dear wiki user,\n"
-"\n"
-"You have subscribed to a wiki page \"%(sitename)s\" for change "
-"notification.\n"
-"\n"
-"The page \"%(pagename)s\" has been renamed from \"%(oldname)s\" by %(editor)"
-"s:\n"
-msgstr ""
-
-#, python-format
-msgid "New user account created on %(sitename)s"
-msgstr ""
-
-#, python-format
-msgid ""
-"Dear Superuser, a new user has just been created. Details follow:\n"
-"\n"
-"    User name: %(username)s\n"
-"    Email address: %(useremail)s"
-msgstr ""
-
-#, python-format
-msgid "New attachment added to page %(pagename)s on %(sitename)s"
-msgstr ""
-
-#, python-format
-msgid ""
-"Dear Wiki user,\n"
-"\n"
-"You have subscribed to a wiki page \"%(page_name)s\" for change "
-"notification. An attachment has been added to that page by %(editor)s. "
-"Following detailed information is available:\n"
-"\n"
-"Attachment name: %(attach_name)s\n"
-"Attachment size: %(attach_size)s\n"
-msgstr ""
-
-#, fuzzy
-msgid "Attachment link"
-msgstr "المرفقات"
-
-#, fuzzy
-msgid "Page link"
-msgstr "حجم الصفحة: %d"
-
-#, fuzzy
-msgid "Changed page"
-msgstr "صفحات مشابه"
-
-#, fuzzy
-msgid "Page changed"
-msgstr "إحفظ التغييرات"
-
-#, python-format
-msgid "Expected \"%(wanted)s\" after \"%(key)s\", got \"%(token)s\""
-msgstr ""
-
-#, python-format
-msgid "Expected an integer \"%(key)s\" before \"%(token)s\""
-msgstr ""
-
-#, python-format
-msgid "Expected an integer \"%(arg)s\" after \"%(key)s\""
-msgstr ""
-
-#, python-format
-msgid "Expected a color value \"%(arg)s\" after \"%(key)s\""
-msgstr ""
-
-msgid "XSLT option disabled, please look at HelpOnConfiguration."
-msgstr ""
-
-msgid "XSLT processing is not available, please install 4suite 1.x."
-msgstr ""
-
-#, python-format
-msgid "%(errortype)s processing error"
-msgstr ""
-
-msgid ""
-"Rendering of reStructured text is not possible, please install Docutils."
-msgstr ""
-
-msgid "**Maximum number of allowed includes exceeded**"
-msgstr ""
-
-#, fuzzy, python-format
-msgid "**You are not allowed to read the page: %s**"
-msgstr "!لا يسمح لك بالعودة إلى هذه الصفحة"
-
-#, python-format
-msgid "**Could not find the referenced page: %s**"
-msgstr ""
+msgid "Password repeat"
+msgstr "اعادة كلمة السر"
 
 msgid "Preferences"
 msgstr "التفضيلات"
@@ -845,6 +559,9 @@
 msgid "Default"
 msgstr "إفتراضي"
 
+msgid "<Browser setting>"
+msgstr "<إعدادات المتصفح>"
+
 msgid "the one preferred"
 msgstr "المفضل"
 
@@ -887,51 +604,6 @@
 msgid "Quick links"
 msgstr "روابط سريعة"
 
-msgid "Switch user"
-msgstr ""
-
-msgid "No user selected"
-msgstr ""
-
-msgid ""
-"You can now change the settings of the selected user account; log out to get "
-"back to your account."
-msgstr ""
-
-msgid "You are the only user."
-msgstr "أنت المستخدم الوحيد."
-
-msgid ""
-"As a superuser, you can temporarily assume the identity of another user."
-msgstr ""
-
-#, fuzzy
-msgid "Select User"
-msgstr "إختر مستخدم"
-
-#, fuzzy
-msgid "Change password"
-msgstr "كلمةالسرّ"
-
-msgid "Passwords don't match!"
-msgstr "كلمات السر غير متطابقة!"
-
-msgid "Please specify a password!"
-msgstr "الرجاء إدخال كلمة سر!"
-
-#, python-format
-msgid "Password not acceptable: %s"
-msgstr "كلمة السر غير مقبولة: %s"
-
-msgid "Your password has been changed."
-msgstr "تم تغيير كلمة السر الخاصة بك."
-
-msgid "To change your password, enter a new password twice."
-msgstr "لتغيير كلمة السر الخاصة بك، أدخل كلمة سر جديدة مرتين."
-
-msgid "Password repeat"
-msgstr "اعادة كلمة السر"
-
 msgid "Notification"
 msgstr ""
 
@@ -963,6 +635,28 @@
 msgid "Subscribed wiki pages<<BR>>(one regex per line)"
 msgstr ""
 
+msgid "Switch user"
+msgstr ""
+
+msgid "No user selected"
+msgstr ""
+
+msgid ""
+"You can now change the settings of the selected user account; log out to get "
+"back to your account."
+msgstr ""
+
+msgid "You are the only user."
+msgstr "أنت المستخدم الوحيد."
+
+msgid ""
+"As a superuser, you can temporarily assume the identity of another user."
+msgstr ""
+
+#, fuzzy
+msgid "Select User"
+msgstr "إختر مستخدم"
+
 msgid "OpenID server"
 msgstr ""
 
@@ -990,31 +684,36 @@
 msgid "OpenID is already present."
 msgstr ""
 
+msgid "Failed to resolve OpenID."
+msgstr ""
+
+msgid "OpenID discovery failure, not a valid OpenID."
+msgstr ""
+
+#, python-format
+msgid "OpenID error: %s."
+msgstr ""
+
+#, fuzzy
+msgid "Verification canceled."
+msgstr "تم إلغاء العملية."
+
 msgid "This OpenID is already used for another account."
 msgstr ""
 
 msgid "OpenID added successfully."
 msgstr ""
 
+msgid "OpenID failure."
+msgstr ""
+
 msgid "Current OpenIDs"
 msgstr ""
 
 msgid "Add OpenID"
 msgstr ""
 
-msgid "Options --pages and --search are mutually exclusive!"
-msgstr ""
-
-msgid "You must specify an output file!"
-msgstr ""
-
-msgid "No pages specified using --pages or --search, assuming full package."
-msgstr ""
-
-msgid "All attachments included into the package."
-msgstr ""
-
-msgid "Output file already exists! Cowardly refusing to continue!"
+msgid "OpenID verification requires that you click this button:"
 msgstr ""
 
 #, fuzzy
@@ -1051,6 +750,9 @@
 msgid "Mail account data"
 msgstr "إبعث لي بيانات حسابي بالبريد الإلكتروني."
 
+msgid "Name"
+msgstr "اسم"
+
 msgid "OpenID"
 msgstr ""
 
@@ -1058,280 +760,263 @@
 msgid "Login"
 msgstr "ولوج"
 
-#, python-format
-msgid "Local Site Map for \"%s\""
-msgstr ""
-
-msgid "Please log in first."
-msgstr "رجاء سجل دخولك أولا."
-
-msgid "Please first create a homepage before creating additional pages."
-msgstr ""
-
-#, python-format
 msgid ""
-"You can add some additional sub pages to your already existing homepage "
-"here.\n"
-"\n"
-"You can choose how open to other readers or writers those pages shall be,\n"
-"access is controlled by group membership of the corresponding group page.\n"
-"\n"
-"Just enter the sub page's name and click on the button to create a new "
-"page.\n"
+"Rendering of reStructured text is not possible, please install Docutils."
+msgstr ""
+
+msgid ""
+"{{{\n"
+"Emphasis: *italic* **bold** ``monospace``\n"
 "\n"
-"Before creating access protected pages, make sure the corresponding group "
-"page\n"
-"exists and has the appropriate members in it. Use HomepageGroupsTemplate for "
-"creating\n"
-"the group pages.\n"
+"Headings: Heading 1  Heading 2  Heading 3\n"
+"          =========  ---------  ~~~~~~~~~\n"
 "\n"
-"||'''Add a new personal page:'''||'''Related access control list "
-"group:'''||\n"
-"||<<NewPage(HomepageReadWritePageTemplate,read-write page,%(username)s)>>||"
-"[[%(username)s/ReadWriteGroup]]||\n"
-"||<<NewPage(HomepageReadPageTemplate,read-only page,%(username)s)>>||[[%"
-"(username)s/ReadGroup]]||\n"
-"||<<NewPage(HomepagePrivatePageTemplate,private page,%(username)s)>>||%"
-"(username)s only||\n"
+"Horizontal rule: ----\n"
 "\n"
-msgstr ""
-
-msgid "MyPages management"
-msgstr ""
-
-#, python-format
-msgid "Please use the interactive user interface to use action %(actionname)s!"
-msgstr ""
-
-msgid "TextCha: Wrong answer! Go back and try again..."
+"Links: TrailingUnderscore_ `multi word with backticks`_ external_\n"
+"\n"
+".. _external: http://external-site.example.org/foo/\n"
+"\n"
+"Lists: * bullets; 1., a. numbered items.\n"
+"}}}\n"
+"(!) For more help, see the\n"
+"[[http://docutils.sourceforge.net/docs/user/rst/quickref.html|"
+"reStructuredText Quick Reference]].\n"
+msgstr ""
+
+msgid "**Maximum number of allowed includes exceeded**"
 msgstr ""
 
 #, fuzzy, python-format
-msgid "You must login to use this action: %(action)s."
-msgstr "يجب عليك تسجيل دخولك لاستعمال الأشتراكات."
-
-msgid "You are not allowed to subscribe to a page you can't read."
-msgstr "لا يسمح لك الإشتراك بصفحة لا تستطيع قرائتها."
-
-msgid "This wiki is not enabled for mail/Jabber processing."
-msgstr ""
-
-msgid "You must log in to use subscriptions."
-msgstr "يجب عليك تسجيل دخولك لاستعمال الأشتراكات."
-
-msgid ""
-"Add your email address or Jabber ID in your user settings to use "
-"subscriptions."
-msgstr ""
-
-#, fuzzy
-msgid "You are already subscribed to this page."
-msgstr "لا يسمح لك بتحرير هذه الصفحة"
-
-msgid "You have been subscribed to this page."
-msgstr ""
-
-#, fuzzy
-msgid "You could not get subscribed to this page."
-msgstr "لا يسمح لك بتحرير هذه الصفحة"
-
-msgid ""
-"Cannot create a new page without a page name.  Please specify a page name."
-msgstr ""
-
-msgid "Do it."
-msgstr "نفذ."
+msgid "**You are not allowed to read the page: %s**"
+msgstr "!لا يسمح لك بالعودة إلى هذه الصفحة"
 
 #, python-format
-msgid "Execute action %(actionname)s?"
-msgstr "نفذ عملية %(actionname)s?"
+msgid "**Could not find the referenced page: %s**"
+msgstr ""
+
+msgid "XSLT option disabled, please look at HelpOnConfiguration."
+msgstr ""
+
+msgid "XSLT processing is not available, please install 4suite 1.x."
+msgstr ""
 
 #, python-format
-msgid "Action %(actionname)s is excluded in this wiki!"
-msgstr "لا يسمح للويكي بتنفيذ عملية %(actionname)s!"
-
-#, python-format
-msgid "You are not allowed to use action %(actionname)s on this page!"
-msgstr "لا يسمح لك باستخدام عملية %(actionname)s في هذه الصفحة!"
-
-msgid "If this account exists an email was sent."
+msgid "%(errortype)s processing error"
 msgstr ""
 
 msgid ""
-"This wiki is not enabled for mail processing.\n"
-"Contact the owner of the wiki, who can enable email."
-msgstr ""
-
-#, fuzzy
-msgid "Please provide a valid email address or a username!"
-msgstr "الرجاء تزويد عنوان بريد إلكتروني صحيح!"
-
-msgid "Mail me my account data"
-msgstr "إبعث لي بيانات حسابي بالبريد الإلكتروني."
-
-msgid "Recovery token"
-msgstr ""
-
-#, fuzzy
-msgid "New password"
-msgstr "كلمةالسرّ"
-
-#, fuzzy
-msgid "New password (repeat)"
-msgstr "اعادة كلمة السر"
-
-#, fuzzy
-msgid "Reset my password"
-msgstr "كلمةالسرّ"
-
-#, fuzzy
-msgid "Your password has been changed, you can log in now."
-msgstr "تم تغيير كلمة السر الخاصة بك."
-
-msgid "Your token is invalid!"
+" Emphasis:: <<Verbatim(//)>>''italics''<<Verbatim(//)>>; <<Verbatim(**)"
+">>'''bold'''<<Verbatim(**)>>; <<Verbatim(**//)>>'''''bold "
+"italics'''''<<Verbatim(//**)>>; <<Verbatim(//)>>''mixed ''<<Verbatim(**)"
+">>'''''bold'''<<Verbatim(**)>> and italics''<<Verbatim(//)>>;\n"
+" Horizontal Rule:: <<Verbatim(----)>>\n"
+" Force Linebreak:: <<Verbatim(\\\\)>>\n"
+" Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; "
+"===== Title 5 =====.\n"
+" Lists:: * bullets; ** sub-bullets; # numbered items; ## numbered sub "
+"items.\n"
+" Links:: <<Verbatim([[target]])>>; <<Verbatim([[target|linktext]])>>.\n"
+" Tables:: |= header text | cell text | more cell text |;\n"
+"\n"
+"(!) For more help, see HelpOnEditing or HelpOnCreoleSyntax.\n"
+msgstr ""
+
+msgid ""
+" Emphasis:: <<Verbatim('')>>''italics''<<Verbatim('')>>; <<Verbatim(''')"
+">>'''bold'''<<Verbatim(''')>>; <<Verbatim(''''')>>'''''bold "
+"italics'''''<<Verbatim(''''')>>; <<Verbatim('')>>''mixed ''<<Verbatim(''')"
+">>'''''bold'''<<Verbatim(''')>> and italics''<<Verbatim('')>>; <<Verbatim"
+"(----)>> horizontal rule.\n"
+" Headings:: = Title 1 =; == Title 2 ==; === Title 3 ===; ==== Title 4 ====; "
+"===== Title 5 =====.\n"
+" Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1."
+"#n start numbering at n; space alone indents.\n"
+" Links:: <<Verbatim(JoinCapitalizedWords)>>; <<Verbatim([[target|linktext]])"
+">>.\n"
+" Tables:: || cell text |||| cell text spanning 2 columns ||;    no trailing "
+"white space allowed after tables or titles.\n"
+"\n"
+"(!) For more help, see HelpOnEditing or SyntaxReference.\n"
+msgstr ""
+
+#, python-format
+msgid "Expected \"%(wanted)s\" after \"%(key)s\", got \"%(token)s\""
+msgstr ""
+
+#, python-format
+msgid "Expected an integer \"%(key)s\" before \"%(token)s\""
+msgstr ""
+
+#, python-format
+msgid "Expected an integer \"%(arg)s\" after \"%(key)s\""
+msgstr ""
+
+#, python-format
+msgid "Expected a color value \"%(arg)s\" after \"%(key)s\""
+msgstr ""
+
+msgid "anonymous"
 msgstr ""
 
 #, fuzzy
-msgid "Password reset"
+msgid "Password is too short."
 msgstr "اعادة كلمة السر"
 
+msgid "Password has not enough different characters."
+msgstr ""
+
 #, fuzzy
 msgid ""
-"\n"
-"== Password reset ==\n"
-"Enter a new password below."
-msgstr "لتغيير كلمة السر الخاصة بك، أدخل كلمة سر جديدة مرتين."
+"Password is too easy (password contains name or name contains password)."
+msgstr "لا يوجد كلمة سر. الرجاء إدخال إسم مستخدم و كلمة سر"
+
+msgid "Password is too easy (keyboard sequence)."
+msgstr ""
+
+msgid "Diffs"
+msgstr ""
+
+msgid "Info"
+msgstr ""
 
 #, fuzzy
-msgid "Lost password"
-msgstr "كلمةالسرّ"
-
-msgid ""
-"\n"
-"== Recovering a lost password ==\n"
-"If you have forgotten your password, provide your email address or\n"
-"username and click on '''Mail me my account data'''.\n"
-"You will receive an email containing a recovery token that can be\n"
-"used to change your password. The email will also contain further\n"
-"instructions."
-msgstr ""
-
-msgid ""
-"\n"
-"=== Password reset ===\n"
-"If you already have received the email with the recovery token, enter your\n"
-"username, the recovery token and a new password (twice) below."
-msgstr ""
+msgid "Edit"
+msgstr "حرّر"
+
+msgid "UnSubscribe"
+msgstr ""
+
+msgid "Subscribe"
+msgstr "اشتراك"
+
+msgid "Raw"
+msgstr ""
+
+msgid "XML"
+msgstr "XML"
+
+msgid "Print"
+msgstr "طباعة"
+
+msgid "View"
+msgstr "اظهر"
+
+msgid "Up"
+msgstr "أعلى"
+
+msgid "Publish my email (not my wiki homepage) in author info"
+msgstr ""
+"انشر رسائلي الإلكترونية (ليس صفحتي الشخصية في الويكي ) في معلومات المؤلف"
+
+msgid "Open editor on double click"
+msgstr "افتح المحرر عند النقر المزدوج"
+
+#, fuzzy
+msgid "After login, jump to last visited page"
+msgstr "بعد الولوج، إذهب إلى آخر صفحة تمت زيارتها"
+
+msgid "Show comment sections"
+msgstr "أظهر أقسام التعليقات"
+
+msgid "Show question mark for non-existing pagelinks"
+msgstr "أظهر علامة سؤال للروابط التي تؤدي إلى صفحات غير موجودة"
+
+msgid "Show page trail"
+msgstr ""
+
+msgid "Show icon toolbar"
+msgstr "اظهر أيقونات شريط الأدوات"
+
+msgid "Show top/bottom links in headings"
+msgstr ""
+
+msgid "Show fancy diffs"
+msgstr ""
+
+msgid "Add spaces to displayed wiki names"
+msgstr ""
+
+msgid "Remember login information"
+msgstr "تذكر معلومات الدخول"
+
+msgid "Disable this account forever"
+msgstr "عطل هذا الحساب للأبد"
+
+#, fuzzy
+msgid "(Use FirstnameLastname)"
+msgstr "(استخدم الاسم الأول الاسم الأخير)"
+
+msgid "Alias-Name"
+msgstr "الأسم المستعار"
+
+msgid "Jabber ID"
+msgstr ""
+
+msgid "User CSS URL"
+msgstr ""
+
+msgid "(Leave it empty for disabling user CSS)"
+msgstr "URL CSS المستخدم"
+