changeset 3973:d2200a05c888

merge from 1.8 main branch
author Byeongweon [tasyblue@gmail.com]
date Thu, 17 Jul 2008 16:29:23 +0900
parents 97dd5fb0c75b (current diff) 44275a3436eb (diff)
children 0347889df050
files MoinMoin/action/backup.py
diffstat 205 files changed, 9392 insertions(+), 5504 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Mon Jul 07 10:18:20 2008 +0900
+++ b/.hgignore	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,11 @@
-.*\.py[co]
+.*\.py[co]$
+^dist/
+^tests/wiki/
+^wiki/underlay/
+^wiki/data/edit-log
+^wiki/data/event-log
+^wiki/data/cache/
+^wikiconfig_local.*
+^MoinMoin/i18n/POTFILES(\.in)?$
 .coverage
-tests/wiki
-wiki/underlay
-wiki/data/edit-log
-wiki/data/event-log
-wiki/data/cache
-wikiconfig_local.*
+
--- a/.hgtags	Mon Jul 07 10:18:20 2008 +0900
+++ b/.hgtags	Thu Jul 17 16:29:23 2008 +0900
@@ -19,3 +19,6 @@
 2b734fe96f5746e621721ce181ac983118222dea 1.7.0beta2
 f126ec68060508be14ddd01f1e35103329b805d1 1.7.0rc1
 00af8e564f9651d0684fb4e93dbe11251d9749ba SOC2008-START
+9901ffff5280b81d0476e8d1e434ea70e3a6fbdc 1.7.0rc2
+01ef230fb671b0f0636328b04ade9b44c5548327 1.7.0rc3
+761c3a503be2b97d8e7beb902751dbb5e60f3127 1.7.0
--- a/Makefile	Mon Jul 07 10:18:20 2008 +0900
+++ b/Makefile	Thu Jul 17 16:29:23 2008 +0900
@@ -25,6 +25,17 @@
         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"
+	sed \
+		-e 's#href="/#href="http://master17.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' \
+		-e 's#/wiki/classic/#wiki/htdocs/classic/#g' \
+		-e 's#/wiki/modern/#wiki/htdocs/modern/#g' \
+		-e 's#/wiki/rightsidebar/#wiki/htdocs/rightsidebar/#g' \
+        build/UPDATE.html >docs/UPDATE.html
+	-rm build/UPDATE.html
 	-rmdir build
 
 interwiki:
@@ -44,9 +55,8 @@
 	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
-	rm -rf $(share)/underlay/pages/InterWikiMap/
-	echo -ne "#acl All:read\r\nSee MoinMoin:EditingOnMoinMaster.\r\n" > \
-	    $(share)/underlay/pages/MoinPagesEditorGroup/revisions/00000001
+	rm -rf $(share)/underlay/pages/InterWikiMap
+	rm -rf $(share)/underlay/pages/MoinPagesEditorGroup
 	cd $(share); rm -f underlay.tar; tar cf underlay.tar underlay
 
 pagepacks:
@@ -66,7 +76,7 @@
 
 # Report translations status
 check-i18n:
-	MoinMoin/i18n/check_i18n.py
+	MoinMoin/i18n/tools/check_i18n.py
 
 # Update the workdir from the default pull repo
 update:
--- a/MoinMoin/PageEditor.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/PageEditor.py	Thu Jul 17 16:29:23 2008 +0900
@@ -483,7 +483,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))
@@ -520,7 +521,7 @@
             request.theme.add_msg(_('Edit was cancelled.'), "error")
             self.send_page()
 
-    def copyPage(self, newpagename, comment=None):
+    def copyPage(self, newpagename, comment=u''):
         """ Copy the current version of the page (keeping the backups, logs and attachments).
 
         @param comment: Comment given by user
@@ -557,8 +558,6 @@
         try:
             filesys.copytree(oldpath, newpath)
             self.error = None
-            if not comment:
-                comment = u"## page was copied from %s" % self.page_name
             savetext = u"## page was copied from %s\n%s" % (self.page_name, savetext)
             Page.__init__(self, request, newpagename)
             self._write_file(savetext, "SAVENEW", comment)
@@ -575,7 +574,7 @@
             else:
                 return False, _('Could not copy page because of file system error: %s.') % unicode(err)
 
-    def renamePage(self, newpagename, comment=None):
+    def renamePage(self, newpagename, comment=u''):
         """ Rename the current version of the page (making a backup before deletion
             and keeping the backups, logs and attachments).
 
@@ -620,8 +619,6 @@
         try:
             os.rename(oldpath, newpath)
             self.error = None
-            if not comment:
-                comment = u"## page was renamed from %s" % self.page_name
             # Save page text with a comment about the old name
             savetext = u"## page was renamed from %s\n%s" % (self.page_name, savetext)
             newpage.saveText(savetext, 0, comment=comment, extra=self.page_name, action='SAVE/RENAME', notify=False)
@@ -1141,7 +1138,7 @@
 
                         if recipients:
                             info = _("Notifications sent to:")
-                            msg = msg + "<p>%s %s</p>" % (info, ",".join(recipients))
+                            msg = msg + "<p>%s %s</p>" % (info, ", ".join(recipients))
 
             # Update page trail with the page we just saved.
             # This is needed for NewPage macro with backto because it does not
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/_tests/ldap_testbase.py	Thu Jul 17 16:29:23 2008 +0900
@@ -0,0 +1,264 @@
+# -*- coding: utf-8 -*-
+"""
+    LDAPTestBase: LDAP testing support for py.test based unit tests
+
+    Features
+    --------
+
+    * setup_class
+      * automatic creation of a temporary LDAP server environment
+      * automatic creation of a LDAP server process (slapd)
+
+    * teardown_class
+      * LDAP server process will be killed and termination will be waited for
+      * temporary LDAP environment will be removed
+
+    Usage
+    -----
+
+    Write your own test class and derive from LDAPTestBase:
+
+    class TestLdap(LDAPTestBase):
+        def testFunction(self):
+            server_url = self.ldap_env.slapd.url
+            lo = ldap.initialize(server_url)
+            lo.simple_bind_s('', '')
+
+    Notes
+    -----
+
+    On Ubuntu 8.04 there is apparmor imposing some restrictions on /usr/sbin/slapd,
+    so you need to disable apparmor by invoking this as root:
+
+    # /etc/init.d/apparmor stop
+
+    @copyright: 2008 by Thomas Waldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+SLAPD_EXECUTABLE = 'slapd'  # filename of LDAP server executable - if it is not
+                            # in your PATH, you have to give full path/filename.
+
+import os, shutil, tempfile, time
+from StringIO import StringIO
+import signal
+
+try:
+    import subprocess  # needs Python 2.4
+except ImportError:
+    subprocess = None
+
+try:
+    import ldap, ldif, ldap.modlist  # needs python-ldap
+except ImportError:
+    ldap = None
+
+
+def check_environ():
+    """ Check the system environment whether we are able to run.
+        Either return some failure reason if we can't or None if everything
+        looks OK.
+    """
+    if subprocess is None:
+        return "You need at least python 2.4 to use ldap_testbase."
+    if ldap is None:
+        return "You need python-ldap installed to use ldap_testbase."
+    slapd = False
+    try:
+        p = subprocess.Popen([SLAPD_EXECUTABLE, '-V'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        pid = p.pid
+        rc = p.wait()
+        if pid and rc == 1:
+            slapd = True  # it works
+    except OSError, err:
+        import errno
+        if not (err.errno == errno.ENOENT or
+                (err.errno == 3 and os.name == 'nt')):
+            raise
+    if not slapd:
+        return "Can't start %s (see SLAPD_EXECUTABLE)." % SLAPD_EXECUTABLE
+    return None
+
+
+class Slapd(object):
+    """ Manage a slapd process for testing purposes """
+    def __init__(self,
+                 config=None,  # config filename for -f
+                 executable=SLAPD_EXECUTABLE,
+                 debug_flags='', # None,  # for -d stats,acl,args,trace,sync,config
+                 proto='ldap', ip='127.0.0.1', port=3890,  # use -h proto://ip:port
+                 service_name=''  # defaults to -n executable:port, use None to not use -n
+                ):
+        self.executable = executable
+        self.config = config
+        self.debug_flags = debug_flags
+        self.proto = proto
+        self.ip = ip
+        self.port = port
+        self.url = '%s://%s:%d' % (proto, ip, port) # can be used for ldap.initialize() call
+        if service_name == '':
+            self.service_name = '%s:%d' % (executable, port)
+        else:
+            self.service_name = service_name
+
+    def start(self, timeout=0):
+        """ start a slapd process and optionally wait up to timeout seconds until it responds """
+        args = [self.executable, '-h', self.url, ]
+        if self.config is not None:
+            args.extend(['-f', self.config])
+        if self.debug_flags is not None:
+            args.extend(['-d', self.debug_flags])
+        if self.service_name:
+            args.extend(['-n', self.service_name])
+        self.process = subprocess.Popen(args)
+        started = None
+        if timeout:
+            lo = ldap.initialize(self.url)
+            ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+            started = False
+            wait_until = time.time() + timeout
+            while time.time() < wait_until:
+                try:
+                    lo.simple_bind_s('', '')
+                    started = True
+                except ldap.SERVER_DOWN, err:
+                    time.sleep(0.1)
+                else:
+                    break
+        return started
+
+    def stop(self):
+        """ stop this slapd process and wait until it has terminated """
+        pid = self.process.pid
+        os.kill(pid, signal.SIGTERM)
+        os.waitpid(pid, 0)
+
+
+class LdapEnvironment(object):
+    """ Manage a (temporary) environment for running a slapd in it """
+
+    # default DB_CONFIG bdb configuration file contents
+    DB_CONFIG = """\
+# STRANGE: if i use those settings, after the test slapd goes to 100% and doesn't terminate on SIGTERM
+# Set the database in memory cache size.
+#set_cachesize 0 10000000 1
+
+# Set log values.
+#set_lg_regionmax 262144
+#set_lg_bsize 262144
+#set_lg_max 10485760
+
+#set_tas_spins 0
+"""
+
+    def __init__(self,
+                 basedn,
+                 rootdn, rootpw,
+                 instance=0,  # use different values when running multiple LdapEnvironments
+                 schema_dir='/etc/ldap/schema',  # directory with schemas
+                 coding='utf-8',  # coding used for config files
+                 timeout=10,  # how long to wait for slapd starting [s]
+                ):
+        self.basedn = basedn
+        self.rootdn = rootdn
+        self.rootpw = rootpw
+        self.instance = instance
+        self.schema_dir = schema_dir
+        self.coding = coding
+        self.ldap_dir = None
+        self.slapd_conf = None
+        self.timeout = timeout
+
+    def create_env(self, slapd_config, db_config=DB_CONFIG):
+        """ create a temporary LDAP server environment in a temp. directory,
+            including writing a slapd.conf (see configure_slapd) and a
+            DB_CONFIG there.
+        """
+        # create directories
+        self.ldap_dir = tempfile.mkdtemp(prefix='LdapEnvironment-%d.' % self.instance)
+        self.ldap_db_dir = os.path.join(self.ldap_dir, 'db')
+        os.mkdir(self.ldap_db_dir)
+
+        # create DB_CONFIG for bdb backend
+        db_config_fname = os.path.join(self.ldap_db_dir, 'DB_CONFIG')
+        f = open(db_config_fname, 'w')
+        f.write(db_config)
+        f.close()
+
+        # create slapd.conf from content template in slapd_config
+        slapd_config = slapd_config % {
+            'ldap_dir': self.ldap_dir,
+            'ldap_db_dir': self.ldap_db_dir,
+            'schema_dir': self.schema_dir,
+            'basedn': self.basedn,
+            'rootdn': self.rootdn,
+            'rootpw': self.rootpw,
+        }
+        if isinstance(slapd_config, unicode):
+            slapd_config = slapd_config.encode(self.coding)
+        self.slapd_conf = os.path.join(self.ldap_dir, "slapd.conf")
+        f = open(self.slapd_conf, 'w')
+        f.write(slapd_config)
+        f.close()
+
+    def start_slapd(self):
+        """ start a slapd and optionally wait until it talks with us """
+        self.slapd = Slapd(config=self.slapd_conf, port=3890+self.instance)
+        started = self.slapd.start(timeout=self.timeout)
+        return started
+
+    def load_directory(self, ldif_content):
+        """ load the directory with the ldif_content (str) """
+        lo = ldap.initialize(self.slapd.url)
+        ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+        lo.simple_bind_s(self.rootdn, self.rootpw)
+
+        class LDIFLoader(ldif.LDIFParser):
+            def handle(self, dn, entry):
+                lo.add_s(dn, ldap.modlist.addModlist(entry))
+
+        loader = LDIFLoader(StringIO(ldif_content))
+        loader.parse()
+
+    def stop_slapd(self):
+        """ stop a slapd """
+        self.slapd.stop()
+
+    def destroy_env(self):
+        """ remove the temporary LDAP server environment """
+        shutil.rmtree(self.ldap_dir)
+
+try:
+    import py.test
+
+    class LDAPTstBase:
+        """ Test base class for py.test based tests which need a LDAP server to talk to.
+
+            Inherit your test class from this base class to test LDAP stuff.
+        """
+
+        # You MUST define these in your derived class:
+        slapd_config = None  # a string with your slapd.conf template
+        ldif_content = None  # a string with your ldif contents
+        basedn = None  # your base DN
+        rootdn = None  # root DN
+        rootpw = None  # root password
+
+        def setup_class(self):
+            """ Create LDAP server environment, start slapd """
+            self.ldap_env = LdapEnvironment(self.basedn, self.rootdn, self.rootpw)
+            self.ldap_env.create_env(slapd_config=self.slapd_config)
+            started = self.ldap_env.start_slapd()
+            if not started:
+                py.test.skip("Failed to start %s process, please see your syslog / log files"
+                             " (and check if stopping apparmor helps, in case you use it)." % SLAPD_EXECUTABLE)
+            self.ldap_env.load_directory(ldif_content=self.ldif_content)
+
+        def teardown_class(self):
+            """ Stop slapd, remove LDAP server environment """
+            self.ldap_env.stop_slapd()
+            self.ldap_env.destroy_env()
+
+except ImportError:
+    pass  # obviously py.test not in use
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/_tests/ldap_testdata.py	Thu Jul 17 16:29:23 2008 +0900
@@ -0,0 +1,109 @@
+BASEDN = "ou=testing,dc=example,dc=org"
+ROOTDN = "cn=root,%s" % BASEDN
+ROOTPW = "secret"
+
+SLAPD_CONFIG = """\
+# See slapd.conf(5) for details on configuration options.
+
+include		%(schema_dir)s/core.schema
+include		%(schema_dir)s/cosine.schema
+include		%(schema_dir)s/inetorgperson.schema
+#include	%(schema_dir)s/misc.schema
+
+moduleload	back_bdb.la
+
+threads 2
+
+# Global access control ###############################################
+
+# Root DSE: allow anyone to read it
+access to dn.base="" by * read
+# Subschema (sub)entry DSE: allow anyone to read it
+access to dn.base="cn=Subschema" by * read
+
+# we don't need restrictive ACLs for tests:
+access to * by * read
+
+allow bind_anon_dn
+
+# Test-Datenbank ou=testing,dc=example,dc=org ################
+
+database	bdb
+
+directory	%(ldap_db_dir)s
+suffix		"%(basedn)s"
+rootdn		"%(rootdn)s"
+rootpw		%(rootpw)s
+lastmod		on
+
+index 		uid eq
+
+checkpoint 200 5
+
+# Entries to cache in memory
+cachesize 500
+# Search results to cache in memory
+idlcachesize 50
+
+sizelimit	-1
+"""
+
+LDIF_CONTENT = """\
+########################################################################
+# regression testing
+########################################################################
+version: 1
+
+dn: ou=testing,dc=example,dc=org
+objectClass: organizationalUnit
+ou: testing
+
+dn: ou=Groups,ou=testing,dc=example,dc=org
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Users,ou=testing,dc=example,dc=org
+objectClass: organizationalUnit
+ou: Users
+
+dn: ou=Unit A,ou=Users,ou=testing,dc=example,dc=org
+objectClass: organizationalUnit
+ou: Unit A
+
+dn: ou=Unit B,ou=Users,ou=testing,dc=example,dc=org
+objectClass: organizationalUnit
+ou: Unit B
+
+dn: uid=usera,ou=Unit A,ou=Users,ou=testing,dc=example,dc=org
+objectClass: account
+objectClass: simpleSecurityObject
+uid: usera
+userPassword: usera
+
+dn: uid=userb,ou=Unit B,ou=Users,ou=testing,dc=example,dc=org
+cn: Vorname Nachname
+objectClass: inetOrgPerson
+sn: Nachname
+uid: userb
+userPassword: userb
+
+dn: cn=Group A,ou=Groups,ou=testing,dc=example,dc=org
+cn: Group A
+member: cn=dummy
+member: uid=usera,ou=Unit A,ou=Users,ou=testing,dc=example,dc=org
+objectClass: groupOfNames
+
+dn: cn=Group B,ou=Groups,ou=testing,dc=example,dc=org
+cn: Group B
+objectClass: groupOfUniqueNames
+uniqueMember: cn=dummy
+uniqueMember: uid=userb,ou=Unit B,ou=Users,ou=testing,dc=example,dc=org
+
+dn: cn=Group C,ou=Groups,ou=testing,dc=example,dc=org
+cn: Group C
+description: Nested group!
+member: cn=dummy
+member: cn=Group A,ou=Groups,ou=testing,dc=example,dc=org
+objectClass: groupOfNames
+"""
+
--- a/MoinMoin/_tests/test_caching.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/_tests/test_caching.py	Thu Jul 17 16:29:23 2008 +0900
@@ -76,6 +76,24 @@
         page._write_file(test_data2)
         assert cache.needsUpdate(page._text_filename())
 
+    def test_filelike_readwrite(self):
+        request = self.request
+        key = 'nooneknowsit'
+        arena = 'somethingfunny'
+        data = "dontcare"
+        cacheentry = caching.CacheEntry(request, arena, key, scope='wiki', do_locking=True,
+                 use_pickle=False, use_encode=True)
+        cacheentry.open(mode='w')
+        cacheentry.write(data)
+        cacheentry.close()
+
+        assert cacheentry.exists()
+
+        cacheentry.open(mode='r')
+        rdata = cacheentry.read()
+        cacheentry.close()
+
+        assert data == rdata
 
 coverage_modules = ['MoinMoin.caching']
 
--- a/MoinMoin/_tests/test_packages.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/_tests/test_packages.py	Thu Jul 17 16:29:23 2008 +0900
@@ -103,7 +103,7 @@
                                 include_underlay=False,
                                 filter=lambda name: not wikiutil.isSystemPage(self.request, name)),
                                 temp)
-        if package:
+        if not package:
             py.test.skip("No user created pages in wiki!")
         assert zipfile.is_zipfile(temp.name)
 
--- a/MoinMoin/_tests/test_user.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/_tests/test_user.py	Thu Jul 17 16:29:23 2008 +0900
@@ -19,17 +19,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 +99,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 +116,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 +127,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 +139,28 @@
 
         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}'
+
     # Helpers ---------------------------------------------------------
 
-    def createUser(self, name, password, charset='utf-8'):
+    def createUser(self, name, password, pwencoded=False):
         """ 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)
+        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():
--- a/MoinMoin/_tests/test_wikiutil.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/_tests/test_wikiutil.py	Thu Jul 17 16:29:23 2008 +0900
@@ -297,6 +297,21 @@
             for val in result[2]:
                 assert val is None or isinstance(val, unicode)
 
+    def testDoubleNameValueSeparator(self):
+        tests = [
+                  # regular and quoting tests
+                  (u'd==4,=3 ',    ([], {u'd': u'=4', u'': u'3'}, [])),
+                  (u'===a,b,c,d',  ([], {u'': u'==a'}, [u'b', u'c', u'd'])),
+                  (u'a,b,===,c,d', ([u'a', u'b'], {u'': u'=='}, [u'c', u'd'])),
+                ]
+
+        def _check(a, e):
+            r = wikiutil.parse_quoted_separated(a)
+            assert r == e
+
+        for args, expected in tests:
+            yield _check, args, expected
+
     def testNoNameValue(self):
         abcd = [u'a', u'b', u'c', u'd']
         tests = [
--- a/MoinMoin/action/AttachFile.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/AttachFile.py	Thu Jul 17 16:29:23 2008 +0900
@@ -331,20 +331,30 @@
                              fmt.text(label_view) +
                              fmt.url(0))
 
-            is_zipfile = zipfile.is_zipfile(fullpath)
-            if is_zipfile:
-                is_package = packages.ZipPackage(request, fullpath).isPackage()
-                if is_package and request.user.isSuperUser():
-                    links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='install')) +
-                                 fmt.text(label_install) +
-                                 fmt.url(0))
-                elif (not is_package and mt.minor == 'zip' and
-                      may_delete and
-                      request.user.may.read(pagename) and
-                      request.user.may.write(pagename)):
-                    links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='unzip')) +
-                                 fmt.text(label_unzip) +
-                                 fmt.url(0))
+            try:
+                is_zipfile = zipfile.is_zipfile(fullpath)
+                if is_zipfile:
+                    is_package = packages.ZipPackage(request, fullpath).isPackage()
+                    if is_package and request.user.isSuperUser():
+                        links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='install')) +
+                                     fmt.text(label_install) +
+                                     fmt.url(0))
+                    elif (not is_package and mt.minor == 'zip' and
+                          may_delete and
+                          request.user.may.read(pagename) and
+                          request.user.may.write(pagename)):
+                        links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='unzip')) +
+                                     fmt.text(label_unzip) +
+                                     fmt.url(0))
+            except RuntimeError:
+                # We don't want to crash with a traceback here (an exception
+                # here could be caused by an uploaded defective zip file - and
+                # if we crash here, the user does not get a UI to remove the
+                # defective zip file again).
+                # RuntimeError is raised by zipfile stdlib module in case of
+                # problems (like inconsistent slash and backslash usage in the
+                # archive).
+                logging.exception("An exception within zip file attachment handling occurred:")
 
             html.append(fmt.listitem(1))
             html.append("[%s]" % "&nbsp;| ".join(links))
@@ -376,28 +386,6 @@
     return _build_filelist(request, pagename, 1, 0)
 
 
-def _subdir_exception(zf):
-    """
-    Checks for the existance of one common subdirectory shared among
-    all files in the zip file. If this is the case, returns a dict of
-    original names to modified names so that such files can be unpacked
-    as the user would expect.
-    """
-
-    b = zf.namelist()
-    if not '/' in b[0]:
-        return False # no directory
-    slashoffset = b[0].index('/')
-    directory = b[0][:slashoffset]
-    for origname in b:
-        if origname.rfind('/') != slashoffset or origname[:slashoffset] != directory:
-            return False # multiple directories or different directory
-    names = {}
-    for origname in b:
-        names[origname] = origname[slashoffset+1:]
-    return names # returns dict of {origname: safename}
-
-
 def error_msg(pagename, request, msg):
     request.theme.add_msg(msg, "error")
     Page(request, pagename).send_page()
@@ -861,88 +849,104 @@
     upload_form(pagename, request, msg=msg)
 
 
-def _do_unzip(pagename, request):
+def _do_unzip(pagename, request, overwrite=False):
     _ = request.getText
-    valid_pathname = lambda name: ('/' not in name) and ('\\' not in name)
+    pagename, filename, fpath = _access_file(pagename, request)
 
-    pagename, filename, fpath = _access_file(pagename, request)
     if not (request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.write(pagename)):
         return _('You are not allowed to unzip attachments of this page.')
+
     if not filename:
         return # error msg already sent in _access_file
 
-    single_file_size = request.cfg.unzip_single_file_size
-    attachments_file_space = request.cfg.unzip_attachments_space
-    attachments_file_count = request.cfg.unzip_attachments_count
-
-    files = _get_files(request, pagename)
-
-    msg = ""
-    if files:
-        fsize = 0.0
-        fcount = 0
-        for f in files:
-            fsize += float(size(request, pagename, f))
-            fcount += 1
-
-        available_attachments_file_space = attachments_file_space - fsize
-        available_attachments_file_count = attachments_file_count - fcount
-
-        if zipfile.is_zipfile(fpath):
-            zf = zipfile.ZipFile(fpath)
-            sum_size_over_all_valid_files = 0.0
-            count_valid_files = 0
-            namelist = _subdir_exception(zf)
-            if not namelist: # if it's not handled by _subdir_exception()
-                # convert normal zf.namelist() to {origname:finalname} dict
-                namelist = {}
-                for name in zf.namelist():
-                    namelist[name] = name
-            for (origname, finalname) in namelist.iteritems():
-                if valid_pathname(finalname):
-                    sum_size_over_all_valid_files += zf.getinfo(origname).file_size
-                    count_valid_files += 1
+    try:
+        if not zipfile.is_zipfile(fpath):
+            return _('The file %(filename)s is not a .zip file.') % {'filename': filename}
 
-            if sum_size_over_all_valid_files > available_attachments_file_space:
-                msg = _("Attachment '%(filename)s' could not be unzipped because"
-                        " the resulting files would be too large (%(space)d kB"
-                        " missing).") % {
-                            'filename': filename,
-                            'space': (sum_size_over_all_valid_files -
-                                available_attachments_file_space) / 1000 }
-            elif count_valid_files > available_attachments_file_count:
-                msg = _("Attachment '%(filename)s' could not be unzipped because"
-                        " the resulting files would be too many (%(count)d "
-                        "missing).") % {
-                            'filename': filename,
-                            'count': (count_valid_files -
-                                available_attachments_file_count) }
+        # determine how which attachment names we have and how much space each is occupying
+        curr_fsizes = dict([(f, size(request, pagename, f)) for f in _get_files(request, pagename)])
+
+        # Checks for the existance of one common prefix path shared among
+        # all files in the zip file. If this is the case, remove the common prefix.
+        # We also prepare a dict of the new filenames->filesizes.
+        zip_path_sep = '/'  # we assume '/' is as zip standard suggests
+        fname_index = None
+        mapping = []
+        new_fsizes = {}
+        zf = zipfile.ZipFile(fpath)
+        for zi in zf.infolist():
+            name = zi.filename
+            if not name.endswith(zip_path_sep):  # a file (not a directory)
+                if fname_index is None:
+                    fname_index = name.rfind(zip_path_sep) + 1
+                    path = name[:fname_index]
+                if (name.rfind(zip_path_sep) + 1 != fname_index  # different prefix len
+                    or
+                    name[:fname_index] != path): # same len, but still different
+                    mapping = []  # zip is not acceptable
+                    break
+                if zi.file_size >= request.cfg.unzip_single_file_size:  # file too big
+                    mapping = []  # zip is not acceptable
+                    break
+                finalname = name[fname_index:]  # remove common path prefix
+                finalname = finalname.decode(config.charset, 'replace')  # replaces trash with \uFFFD char
+                mapping.append((name, finalname))
+                new_fsizes[finalname] = zi.file_size
+
+        # now we either have an empty mapping (if the zip is not acceptable),
+        # an identity mapping (no subdirs in zip, just all flat), or
+        # a mapping (origname, finalname) where origname is the zip member filename
+        # (including some prefix path) and finalname is a simple filename.
+
+        # calculate resulting total file size / count after unzipping:
+        if overwrite:
+            curr_fsizes.update(new_fsizes)
+            total = curr_fsizes
+        else:
+            new_fsizes.update(curr_fsizes)
+            total = new_fsizes
+        total_count = len(total)
+        total_size = sum(total.values())
+
+        if not mapping:
+            msg = _("Attachment '%(filename)s' not unzipped because some files in the zip "
+                    "are either not in the same directory or exceeded the single file size limit (%(maxsize_file)d kB)."
+                   ) % {'filename': filename,
+                        'maxsize_file': request.cfg.unzip_single_file_size / 1000, }
+        elif total_size > request.cfg.unzip_attachments_space:
+            msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
+                    "the per page attachment storage size limit (%(size)d kB).") % {
+                        'filename': filename,
+                        'size': request.cfg.unzip_attachments_space / 1000, }
+        elif total_count > request.cfg.unzip_attachments_count:
+            msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
+                    "the per page attachment count limit (%(count)d).") % {
+                        'filename': filename,
+                        'count': request.cfg.unzip_attachments_count, }
+        else:
+            not_overwritten = []
+            for origname, finalname in mapping:
+                try:
+                    # Note: reads complete zip member file into memory. ZipFile does not offer block-wise reading:
+                    add_attachment(request, pagename, finalname, zf.read(origname), overwrite)
+                except AttachmentAlreadyExists:
+                    not_overwritten.append(finalname)
+            if not_overwritten:
+                msg = _("Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)s).") % {
+                        'filename': filename,
+                        'filelist': ', '.join(not_overwritten), }
             else:
-                valid_name = False
-                for (origname, finalname) in namelist.iteritems():
-                    if valid_pathname(finalname):
-                        zi = zf.getinfo(origname)
-                        if zi.file_size < single_file_size:
-                            new_file = getFilename(request, pagename, finalname)
-                            if not os.path.exists(new_file):
-                                outfile = open(new_file, 'wb')
-                                outfile.write(zf.read(origname))
-                                outfile.close()
-                                # it's not allowed to zip a zip file so it is dropped
-                                if zipfile.is_zipfile(new_file):
-                                    os.unlink(new_file)
-                                else:
-                                    valid_name = True
-                                    _addLogEntry(request, 'ATTNEW', pagename, finalname)
-
-                if valid_name:
-                    msg = _("Attachment '%(filename)s' unzipped.") % {'filename': filename}
-                else:
-                    msg = _("Attachment '%(filename)s' not unzipped because the "
-                            "files are too big, .zip files only, exist already or "
-                            "reside in folders.") % {'filename': filename}
-        else:
-            msg = _('The file %(filename)s is not a .zip file.') % {'filename': filename}
+                msg = _("Attachment '%(filename)s' unzipped.") % {'filename': filename}
+    except RuntimeError, err:
+        # We don't want to crash with a traceback here (an exception
+        # here could be caused by an uploaded defective zip file - and
+        # if we crash here, the user does not get a UI to remove the
+        # defective zip file again).
+        # RuntimeError is raised by zipfile stdlib module in case of
+        # problems (like inconsistent slash and backslash usage in the
+        # archive).
+        logging.exception("An exception within zip file attachment handling occurred:")
+        msg = _("A severe error occurred:") + ' ' + str(err)
 
     upload_form(pagename, request, msg=wikiutil.escape(msg))
 
@@ -993,18 +997,29 @@
         request.write(request.formatter.preformatted(0))
         return
 
-    package = packages.ZipPackage(request, fpath)
-    if package.isPackage():
-        request.write("<pre><b>%s</b>\n%s</pre>" % (_("Package script:"), wikiutil.escape(package.getScript())))
-        return
+    try:
+        package = packages.ZipPackage(request, fpath)
+        if package.isPackage():
+            request.write("<pre><b>%s</b>\n%s</pre>" % (_("Package script:"), wikiutil.escape(package.getScript())))
+            return
 
-    if zipfile.is_zipfile(fpath) and mt.minor == 'zip':
-        zf = zipfile.ZipFile(fpath, mode='r')
-        request.write("<pre>%-46s %19s %12s\n" % (_("File Name"), _("Modified")+" "*5, _("Size")))
-        for zinfo in zf.filelist:
-            date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
-            request.write(wikiutil.escape("%-46s %s %12d\n" % (zinfo.filename, date, zinfo.file_size)))
-        request.write("</pre>")
+        if zipfile.is_zipfile(fpath) and mt.minor == 'zip':
+            zf = zipfile.ZipFile(fpath, mode='r')
+            request.write("<pre>%-46s %19s %12s\n" % (_("File Name"), _("Modified")+" "*5, _("Size")))
+            for zinfo in zf.filelist:
+                date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
+                request.write(wikiutil.escape("%-46s %s %12d\n" % (zinfo.filename, date, zinfo.file_size)))
+            request.write("</pre>")
+            return
+    except RuntimeError:
+        # We don't want to crash with a traceback here (an exception
+        # here could be caused by an uploaded defective zip file - and
+        # if we crash here, the user does not get a UI to remove the
+        # defective zip file again).
+        # RuntimeError is raised by zipfile stdlib module in case of
+        # problems (like inconsistent slash and backslash usage in the
+        # archive).
+        logging.exception("An exception within zip file attachment handling occurred:")
         return
 
     from MoinMoin import macro
--- a/MoinMoin/action/MyPages.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/MyPages.py	Thu Jul 17 16:29:23 2008 +0900
@@ -48,8 +48,8 @@
 the group pages.
 
 ||'''Add a new personal page:'''||'''Related access control list group:'''||
-||<<NewPage(HomepageReadWritePageTemplate,read-write page,%(username)s)>>||["%(username)s/ReadWriteGroup"]||
-||<<NewPage(HomepageReadPageTemplate,read-only page,%(username)s)>>||["%(username)s/ReadGroup"]||
+||<<NewPage(HomepageReadWritePageTemplate,read-write page,%(username)s)>>||[[%(username)s/ReadWriteGroup]]||
+||<<NewPage(HomepageReadPageTemplate,read-only page,%(username)s)>>||[[%(username)s/ReadGroup]]||
 ||<<NewPage(HomepagePrivatePageTemplate,private page,%(username)s)>>||%(username)s only||
 
 """)
@@ -64,14 +64,14 @@
     request.setContentLanguage(request.lang)
     request.theme.send_title(_('MyPages management'), page=homepage)
 
-    # Start content - IMPORTANT - without content div, there is no direction support!
-    request.write(request.formatter.startContent("content"))
-
     parser = WikiParser(pagecontent, request)
     p = Page(request, "$$$")
     request.formatter.setPage(p)
     parser.format(request.formatter)
 
+    # Start content - IMPORTANT - without content div, there is no direction support!
+    request.write(request.formatter.startContent("content"))
+
     request.write(request.formatter.endContent())
     request.theme.send_footer(homepage.page_name)
     request.theme.send_closing_html()
--- a/MoinMoin/action/PackagePages.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/PackagePages.py	Thu Jul 17 16:29:23 2008 +0900
@@ -217,7 +217,7 @@
                     zipname = "%d_attachment" % cnt
                     script.append(packLine(["AddAttachment", zipname, attname, page.page_name, userid, "Created by the PackagePages action."]))
                     filename = AttachFile.getFilename(self.request, page.page_name, attname)
-                    zf.write(filename.encode("cp437"), zipname)
+                    zf.write(filename, zipname)
         script += [packLine(['Print', 'Thank you for using PackagePages!'])]
 
         zf.writestr(MOIN_PACKAGE_FILE, u"\n".join(script).encode("utf-8"))
--- a/MoinMoin/action/SyncPages.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/SyncPages.py	Thu Jul 17 16:29:23 2008 +0900
@@ -255,7 +255,7 @@
             match_direction = None
 
         local_full_iwid = packLine([local.get_iwid(), local.get_interwiki_name()])
-        remote_full_iwid = packLine([remote.get_iwid(), remote.get_interwiki_name()])
+        remote_full_iwid = remote.iwid_full
 
         self.log_status(self.INFO, _("Synchronisation started -"), raw_suffix=" <<DateTime(%s)>>" % self.page._get_local_timestamp())
 
--- a/MoinMoin/action/_tests/test_attachfile.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/_tests/test_attachfile.py	Thu Jul 17 16:29:23 2008 +0900
@@ -3,50 +3,74 @@
     MoinMoin - tests of AttachFile action
 
     @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
-                     MoinMoin:ReimarBauer
+                2007-2008 MoinMoin:ReimarBauer
     @license: GNU GPL, see COPYING for details.
 """
-import os
+import os, StringIO
 from MoinMoin.action import AttachFile
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin._tests import become_trusted, create_page, nuke_page
 
-def test_add_attachment(request):
-    """Test if add_attachment() works"""
-
-    become_trusted(request)
-    pagename = "AutoCreatedSillyPageToTestAttachments"
-    filename = "AutoCreatedSillyAttachment"
-
-    create_page(request, pagename, u"Foo!")
-
-    AttachFile.add_attachment(request, pagename, filename, "Test content", True)
-    exists = AttachFile.exists(request, pagename, filename)
-
-    nuke_page(request, pagename)
-
-    assert exists
+class TestAttachFile:
+    """ testing action AttachFile"""
+    pagename = u"AutoCreatedSillyPageToTestAttachments"
 
-def test_get_attachment_path_created_on_getFilename(request):
-    """
-    Tests if AttachFile.getFilename creates the attachment dir on requesting
-    """
-    pagename = "ThisPageDoesOnlyExistForThisTest"
-    filename = ""
-    file_exists = os.path.exists(AttachFile.getFilename(request, pagename, filename))
-
-    nuke_page(request, pagename)
+    def test_add_attachment(self):
+        """Test if add_attachment() works"""
 
-    assert file_exists
+        become_trusted(self.request)
+        filename = "AutoCreatedSillyAttachment"
 
-def test_getAttachUrl(request):
-    """
-    Tests if AttachFile.getAttachUrl taints a filename
-    """
-    pagename = "ThisPageDoesOnlyExistForThisTest"
-    filename = "<test2.txt>"
-    expect = "rename=_test2.txt_&"
-    result = AttachFile.getAttachUrl(pagename, filename, request, upload=True)
+        create_page(self.request, self.pagename, u"Foo!")
 
-    assert expect in result
+        AttachFile.add_attachment(self.request, self.pagename, filename, "Test content", True)
+        exists = AttachFile.exists(self.request, self.pagename, filename)
 
+        nuke_page(self.request, self.pagename)
+
+        assert exists
+
+    def test_add_attachment_for_file_object(self):
+        """Test if add_attachment() works with file like object"""
+
+        become_trusted(self.request)
+
+        filename = "AutoCreatedSillyAttachment.png"
+
+        create_page(self.request, self.pagename, u"FooBar!")
+        data = "Test content"
+
+        filecontent = StringIO.StringIO(data)
+
+        AttachFile.add_attachment(self.request, self.pagename, filename, filecontent, True)
+        exists = AttachFile.exists(self.request, self.pagename, filename)
+        path = AttachFile.getAttachDir(self.request, self.pagename)
+        imagef = os.path.join(path, filename)
+        file_size = os.path.getsize(imagef)
+
+        nuke_page(self.request, self.pagename)
+
+        assert exists and file_size == len(data)
+
+    def test_get_attachment_path_created_on_getFilename(self):
+        """
+        Tests if AttachFile.getFilename creates the attachment dir on self.requesting
+        """
+        filename = ""
+        file_exists = os.path.exists(AttachFile.getFilename(self.request, self.pagename, filename))
+
+        nuke_page(self.request, self.pagename)
+
+        assert file_exists
+
+    def test_getAttachUrl(self):
+        """
+        Tests if AttachFile.getAttachUrl taints a filename
+        """
+        filename = "<test2.txt>"
+        expect = "rename=_test2.txt_&"
+        result = AttachFile.getAttachUrl(self.pagename, filename, self.request, upload=True)
+
+        assert expect in result
+
+coverage_modules = ['MoinMoin.action.AttachFile']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/_tests/test_cache.py	Thu Jul 17 16:29:23 2008 +0900
@@ -0,0 +1,179 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - tests of cache action functions
+
+    @copyright: 2008 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import os, StringIO
+
+from MoinMoin import caching
+from MoinMoin.action import AttachFile, cache
+
+from MoinMoin._tests import become_trusted, create_page, nuke_page
+
+class TestSendCached:
+    """ testing action cache """
+    pagename = u"AutoCreatedSillyPageToTestAttachments"
+
+    def test_cache_key_content(self):
+        request = self.request
+        result1 = cache.key(request, content='foo', secret='bar')
+        result2 = cache.key(request, content='foo', secret='baz')
+        assert result1  # not empty
+        assert result1 != result2  # different for different secret
+        result3 = cache.key(request, content='foofoo', secret='baz')
+        assert result3 != result2  # different for different content
+        result4 = cache.key(request, content='foo'*1000, secret='baz')
+        assert len(result4) == len(result3)  # same length of key for different input lengths
+
+    def test_cache_key_attachment(self):
+        request = self.request
+        pagename = self.pagename
+        attachname = 'foo.txt'
+
+        become_trusted(request)
+        create_page(request, pagename, u"Foo!")
+
+        AttachFile.add_attachment(request, pagename, attachname, "Test content1", True)
+
+        result1 = cache.key(request, itemname=pagename, attachname=attachname, secret='bar')
+        result2 = cache.key(request, itemname=pagename, attachname=attachname, secret='baz')
+        assert result1  # not empty
+        assert result1 != result2  # different for different secret
+
+        # test below does not work, because mtime is often same, inode can be same due to how add_attachment
+        # works, file size is same, attachment name is same, wikiname/pagename is same.
+        # In practice, this should rather rarely cause problems:
+        #AttachFile.add_attachment(request, pagename, attachname, "Test content2", True)
+        #result3 = cache.key(request, itemname=pagename, attachname=attachname, secret='baz')
+        #assert result3 != result2  # different for different content
+
+        AttachFile.add_attachment(request, pagename, attachname, "Test content33333", True)
+        result4 = cache.key(request, itemname=pagename, attachname=attachname, secret='baz')
+        assert len(result4) == len(result2)  # same length of key for different input lengths
+        nuke_page(request, pagename)
+
+    def test_put_cache_minimal(self):
+        """Test if put_cache() works"""
+        request = self.request
+        key = 'nooneknowsit'
+        data = "dontcare"
+        url = cache.put(request, key, data)
+
+        assert key in url
+        meta_cache = caching.CacheEntry(request,
+                                        arena=cache.cache_arena,
+                                        scope=cache.cache_scope,
+                                        key=key+'.meta', use_pickle=True)
+        last_modified, headers = meta_cache.content()
+        assert last_modified.endswith(' GMT') # only a very rough check, it has used cache mtime as last_modified
+        assert "Content-Type: application/octet-stream" in headers
+        assert "Content-Length: %d" % len(data) in headers
+
+    def test_put_cache_guess_ct_give_lm(self):
+        """Test if put_cache() works, when we give filename (so it guesses content_type) and last_modified"""
+        request = self.request
+        key = 'nooneknowsit'
+        filename = "test.png"
+        data = "dontcare"
+        url = cache.put(request, key, data, filename=filename, last_modified=1)
+        assert key in url
+
+        meta_cache = caching.CacheEntry(request,
+                                        arena=cache.cache_arena,
+                                        scope=cache.cache_scope,
+                                        key=key+'.meta', use_pickle=True)
+        last_modified, headers = meta_cache.content()
+        assert last_modified == 'Thu, 01 Jan 1970 00:00:01 GMT'
+        assert "Content-Type: image/png" in headers
+        assert "Content-Length: %d" % len(data) in headers
+
+    def test_put_cache_file_like_data(self):
+        """Test if put_cache() works when we give it a file like object for the content"""
+        request = self.request
+        key = 'nooneknowsit'
+        filename = "test.png"
+        data = "dontcareatall"
+        data_file = StringIO.StringIO(data)
+        url = cache.put(request, key, data_file)
+
+        assert key in url
+        meta_cache = caching.CacheEntry(request,
+                                        arena=cache.cache_arena,
+                                        scope=cache.cache_scope,
+                                        key=key+'.meta', use_pickle=True)
+        last_modified, headers = meta_cache.content()
+        assert last_modified.endswith(' GMT') # only a very rough check, it has used cache mtime as last_modified
+        assert "Content-Type: application/octet-stream" in headers
+        assert "Content-Length: %d" % len(data) in headers
+
+        data_cache = caching.CacheEntry(request,
+                                        arena=cache.cache_arena,
+                                        scope=cache.cache_scope,
+                                        key=key+'.data')
+        cached = data_cache.content()
+        assert data == cached
+
+    def test_put_cache_complex(self):
+        """Test if put_cache() works for a more complex, practical scenario:
+
+           As 'source' we just use some random integer as count value.
+
+           The 'rendered representation' of it is just the word "spam" repeated
+           count times, which we cache.
+
+           The cache key calculation (for the 'non-guessable' keys) is also
+           rather simple.
+
+           In real world, source would be likely some big image, rendered
+           representation of it a thumbnail / preview of it. Or some LaTeX
+           source and its rendered representation as png image.
+           Key calculation could be some MAC or some other hard to guess and
+           unique string.
+        """
+        import random
+        request = self.request
+        render = lambda data: "spam" * data
+        secret = 4223
+        keycalc = lambda data: str(data * secret)
+
+        source = random.randint(1, 100)
+        rendered1 = render(source)
+        key1 = keycalc(source)
+
+        url1 = cache.put(request, key1, rendered1)
+        assert 'key=%s' % key1 in url1
+
+        data_cache = caching.CacheEntry(request,
+                                        arena=cache.cache_arena,
+                                        scope=cache.cache_scope,
+                                        key=key1+'.data')
+        cached1 = data_cache.content()
+
+        assert render(source) == cached1
+        # if that succeeds, we have stored the rendered representation of source in the cache under key1
+
+        # now we use some different source, render it and store it in the cache
+        source = source * 2
+        rendered2 = render(source)
+        key2 = keycalc(source)
+
+        url2 = cache.put(request, key2, rendered2)
+        assert 'key=%s' % key2 in url2
+
+        data_cache = caching.CacheEntry(request,
+                                        arena=cache.cache_arena,
+                                        scope=cache.cache_scope,
+                                        key=key2+'.data')
+        cached2 = data_cache.content()
+
+        assert render(source) == cached2
+        # if that succeeds, we have stored the rendered representation of updated source in the cache under key2
+
+        assert url2 != url1  # URLs must be different for different source (implies different keys)
+
+
+coverage_modules = ['MoinMoin.action.cache']
+
--- a/MoinMoin/action/backup.py	Mon Jul 07 10:18:20 2008 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - make or restore a full backup of the wiki
-
-    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.
-
-    @copyright: 2005 by MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import os, re, time
-
-from MoinMoin import wikiutil
-from MoinMoin.support import tarfile
-
-def addFiles(path, tar, exclude):
-    """ 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):
-                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, ])
-
-    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)
-    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')
-    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.
-
- * To make a backup, just hit the <<GetText(Backup)>> button and save the file
-   you get to a secure place.
-
-Please make sure your wiki configuration backup_* values are correct and complete.
-
-""", wiki=True))
-
-    request.write("""
-<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="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):
-        return sendMsg(request, pagename,
-                       msg=_('You are not allowed to do remote backup.'), msgtype="error")
-
-    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")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/cache.py	Thu Jul 17 16:29:23 2008 +0900
@@ -0,0 +1,220 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Send a raw object from the caching system (and offer utility
+    functions to put data into cache, calculate cache key, etc.).
+
+    This can be used e.g. for all image generating extensions:
+    E.g. a thumbnail generating extension just uses cache.put() to
+    write the thumbnails into the cache and emits <img src="cache_url">
+    to display them. cache_url is returned by put() or url().
+
+    IMPORTANT: use some non-guessable key derived from your source content,
+               use cache.key() if you don't have something better.
+
+    TODO:
+    * add secret to wikiconfig
+    * add error handling
+    * maybe use page local caching, not global:
+      + smaller directories
+      - but harder to clean
+      - harder to backup data_dir
+    * move file-like code to caching module
+    * add auto-key generation?
+
+    @copyright: 2008 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import hmac, sha
+
+from MoinMoin import log
+logging = log.getLogger(__name__)
+
+# keep both imports below as they are, order is important:
+from MoinMoin import wikiutil
+import mimetypes
+
+from MoinMoin import config, caching
+from MoinMoin.util import filesys
+from MoinMoin.action import AttachFile
+
+action_name = __name__.split('.')[-1]
+
+# Do NOT get this directly from request.form or user would be able to read any cache!
+cache_arena = 'sendcache'  # just using action_name is maybe rather confusing
+cache_scope = 'wiki'
+do_locking = False
+
+def key(request, wikiname=None, itemname=None, attachname=None, content=None, secret=None):
+    """
+    Calculate a (hard-to-guess) cache key.
+
+    If content is supplied, we will calculate and return a hMAC of the content.
+
+    If wikiname, itemname, attachname is given, we don't touch the content (nor do
+    we read it ourselves from the attachment file), but we just calculate a key
+    from the given metadata values and some metadata we get from the filesystem.
+
+    Hint: if you need multiple cache objects for the same source content (e.g.
+          thumbnails of different sizes for the same image), calculate the key
+          only once and then add some different prefixes to it to get the final
+          cache keys.
+
+    @param request: the request object
+    @param wikiname: the name of the wiki (if not given, will be read from cfg)
+    @param itemname: the name of the page
+    @param attachname: the filename of the attachment
+    @param content: content data as unicode object (e.g. for page content or
+                    parser section content)
+    """
+    secret = secret or 'nobodyexpectedsuchasecret'
+    if content:
+        hmac_data = content
+    elif itemname is not None and attachname is not None:
+        wikiname = wikiname or request.cfg.interwikiname or request.cfg.siteid
+        fuid = filesys.fuid(AttachFile.getFilename(request, itemname, attachname))
+        hmac_data = u''.join([wikiname, itemname, attachname, repr(fuid)])
+    else:
+        raise AssertionError('cache_key called with unsupported parameters')
+
+    hmac_data = hmac_data.encode('utf-8')
+    key = hmac.new(secret, hmac_data, sha).hexdigest()
+    return key
+
+
+def put(request, key, data,
+        filename=None,
+        content_type=None,
+        content_disposition=None,
+        content_length=None,
+        last_modified=None,
+        bufsize=8192):
+    """
+    Put an object into the cache to send it with cache action later.
+
+    @param request: the request object
+    @param key: non-guessable key into cache (str)
+    @param data: content data (str or open file-like obj)
+    @param filename: filename for content-disposition header and for autodetecting
+                     content_type (unicode, default: None)
+    @param content_disposition: type for content-disposition header (str, default: None)
+    @param content_type: content-type header value (str, default: autodetect from filename)
+    @param last_modified: last modified timestamp (int, default: autodetect)
+    @param content_length: data length for content-length header (int, default: autodetect)
+    @return: URL of cached object
+    """
+    import os.path
+    from MoinMoin.util import timefuncs
+
+    if filename:
+        # make sure we just have a simple filename (without path)
+        filename = os.path.basename(filename)
+
+        if content_type is None:
+            # try autodetect
+            mt, enc = mimetypes.guess_type(filename)
+            if mt:
+                content_type = mt
+
+    if content_type is None:
+        content_type = 'application/octet-stream'
+
+    data_cache = caching.CacheEntry(request, cache_arena, key+'.data', cache_scope, do_locking=do_locking)
+    data_cache.update(data)
+    content_length = content_length or data_cache.size()
+    last_modified = last_modified or data_cache.mtime()
+
+    last_modified = timefuncs.formathttpdate(int(last_modified))
+    headers = ['Content-Type: %s' % content_type,
+               'Last-Modified: %s' % last_modified,
+               'Content-Length: %s' % content_length,
+              ]
+    if content_disposition and filename:
+        # TODO: fix the encoding here, plain 8 bit is not allowed according to the RFCs
+        # There is no solution that is compatible to IE except stripping non-ascii chars
+        filename = filename.encode(config.charset)
+        headers.append('Content-Disposition: %s; filename="%s"' % (content_disposition, filename))
+
+    meta_cache = caching.CacheEntry(request, cache_arena, key+'.meta', cache_scope, do_locking=do_locking, use_pickle=True)
+    meta_cache.update((last_modified, headers))
+
+    return url(request, key, do='get')
+
+
+def exists(request, key, strict=False):
+    """
+    Check if a cached object for this key exists.
+
+    @param request: the request object
+    @param key: non-guessable key into cache (str)
+    @param strict: if True, also check the data cache, not only meta (bool, default: False)
+    @return: is object cached? (bool)
+    """
+    if strict:
+        data_cache = caching.CacheEntry(request, cache_arena, key+'.data', cache_scope, do_locking=do_locking)
+        data_cached = data_cache.exists()
+    else:
+        data_cached = True  # we assume data will be there if meta is there
+
+    meta_cache = caching.CacheEntry(request, cache_arena, key+'.meta', cache_scope, do_locking=do_locking, use_pickle=True)
+    meta_cached = meta_cache.exists()
+
+    return meta_cached and data_cached
+
+
+def remove(request, key):
+    """ delete headers/data cache for key """
+    meta_cache = caching.CacheEntry(request, cache_arena, key+'.meta', cache_scope, do_locking=do_locking, use_pickle=True)
+    meta_cache.remove()
+    data_cache = caching.CacheEntry(request, cache_arena, key+'.data', cache_scope, do_locking=do_locking)
+    data_cache.remove()
+
+
+def url(request, key, do='get'):
+    """ return URL for the object cached for key """
+    return "%s/?%s" % (
+        request.getScriptname(),
+        wikiutil.makeQueryString(dict(action=action_name, do=do, key=key), want_unicode=False))
+
+
+def _get_headers(request, key):
+    """ get last_modified and headers cached for key """
+    meta_cache = caching.CacheEntry(request, cache_arena, key+'.meta', cache_scope, do_locking=do_locking, use_pickle=True)
+    last_modified, headers = meta_cache.content()
+    return last_modified, headers
+
+
+def _get_datafile(request, key):
+    """ get an open data file for the data cached for key """
+    data_cache = caching.CacheEntry(request, cache_arena, key+'.data', cache_scope, do_locking=do_locking)
+    data_cache.open(mode='r')
+    return data_cache
+
+
+def _do_get(request, key):
+    """ send a complete http response with headers/data cached for key """
+    last_modified, headers = _get_headers(request, key)
+    if request.if_modified_since == last_modified:
+        request.emit_http_headers(["Status: 304 Not modified"])
+    else:
+        request.emit_http_headers(headers)
+        request.send_file(_get_datafile(request, key))
+
+
+def _do_remove(request, key):
+    """ delete headers/data cache for key """
+    remove(request, key)
+    request.emit_http_headers(["Status: 200 OK"])
+
+
+def _do(request, do, key):
+    if do == 'get':
+        _do_get(request, key)
+    elif do == 'remove':
+        _do_remove(request, key)
+
+def execute(pagename, request):
+    do = request.form.get('do', [None])[0]
+    key = request.form.get('key', [None])[0]
+    _do(request, do, key)
+
--- a/MoinMoin/action/info.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/info.py	Thu Jul 17 16:29:23 2008 +0900
@@ -207,8 +207,14 @@
         request.write("[%s] " % page.link_to(request, text=text, querystr=querystr, rel='nofollow'))
     request.write(f.paragraph(0))
 
-    show_hitcounts = int(request.form.get('hitcounts', [0])[0]) != 0
-    show_general = int(request.form.get('general', [0])[0]) != 0
+    try:
+        show_hitcounts = int(request.form.get('hitcounts', [0])[0]) != 0
+    except ValueError:
+        show_hitcounts = False
+    try:
+        show_general = int(request.form.get('general', [0])[0]) != 0
+    except ValueError:
+        show_general = False
 
     if show_hitcounts:
         from MoinMoin.stats import hitcounts
--- a/MoinMoin/action/newaccount.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/newaccount.py	Thu Jul 17 16:29:23 2008 +0900
@@ -10,6 +10,7 @@
 from MoinMoin.Page import Page
 from MoinMoin.widget import html
 from MoinMoin.security.textcha import TextCha
+from MoinMoin.auth import MoinAuth
 
 
 _debug = False
@@ -82,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)
@@ -106,8 +104,10 @@
     tbl.append(row)
     row.append(html.TD().append(html.STRONG().append(
                                   html.Text(_("Name")))))
-    row.append(html.TD().append(html.INPUT(type="text", size="36",
-                                           name="name")))
+    cell = html.TD()
+    row.append(cell)
+    cell.append(html.INPUT(type="text", size="36", name="name"))
+    cell.append(html.Text(' ' + _("(Use FirstnameLastname)")))
 
     row = html.TR()
     tbl.append(row)
@@ -145,23 +145,28 @@
     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)
 
 def execute(pagename, request):
-    pagename = pagename
+    found = False
+    for auth in request.cfg.auth:
+        if isinstance(auth, MoinAuth):
+            found = True
+            break
+
+    if not found:
+        # we will not have linked, so forbid access
+        request.makeForbidden403()
+        return
+
     page = Page(request, pagename)
     _ = 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/recoverpass.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/action/recoverpass.py	Thu Jul 17 16:29:23 2008 +0900
@@ -9,6 +9,7 @@
 from MoinMoin import user, wikiutil
 from MoinMoin.Page import Page
 from MoinMoin.widget import html
+from MoinMoin.auth import MoinAuth
 
 def _do_email(request, u):
     _ = request.getText
@@ -138,7 +139,17 @@
 
 
 def execute(pagename, request):
-    pagename = pagename
+    found = False
+    for auth in request.cfg.auth:
+        if isinstance(auth, MoinAuth):
+            found = True
+            break
+
+    if not found:
+        # we will not have linked, so forbid access
+        request.makeForbidden403()
+        return
+
     page = Page(request, pagename)
     _ = request.getText
     form = request.form
--- a/MoinMoin/auth/__init__.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/auth/__init__.py	Thu Jul 17 16:29:23 2008 +0900
@@ -60,6 +60,12 @@
     methods will use the message feature only along with returning False for
     the continue flag.
 
+    Note, however, that when no username is entered or the username is not
+    found in the database, it may be appropriate to return with a message
+    and the continue flag set to true (ContinueLogin) because a subsequent auth
+    plugin might work even without the username, say the openid plugin for
+    example.
+
     The multistage member must evaluate to false or be callable. If it is
     callable, this indicates that the authentication method requires a second
     login stage. In that case, the multistage item will be called with the
@@ -207,13 +213,13 @@
     def login_hint(self, request):
         return None
 
-class MoinLogin(BaseAuth):
+class MoinAuth(BaseAuth):
     """ handle login from moin login form """
     def __init__(self):
         BaseAuth.__init__(self)
 
     login_inputs = ['username', 'password']
-    name = 'moin_login'
+    name = 'moin'
     logout_possible = True
 
     def login(self, request, user_obj, **kw):
--- a/MoinMoin/auth/_tests/test_auth.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/auth/_tests/test_auth.py	Thu Jul 17 16:29:23 2008 +0900
@@ -204,13 +204,13 @@
             trail = request.session['trail']
             assert trail == trail_expected
 
-    def testMoinLoginAuthSession(self):
-        """ run some requests with moin_login auth, check whether session works """
-        from MoinMoin.auth import MoinLogin
+    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=[MoinLogin()])
-        username = u'MoinLoginAuthTestUser'
-        password = u'secret'
+        self.config = self.TestConfig(auth=[MoinAuth()])
+        username = u'MoinAuthTestUser'
+        password = u'ßecretß'
         User(self.request, name=username, password=password).save() # create user
         trail_expected = []
         first = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/auth/_tests/test_ldap_login.py	Thu Jul 17 16:29:23 2008 +0900
@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+"""
+    MoinMoin - MoinMoin.auth.ldap Tests
+
+    @copyright: 2008 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import py.test
+
+from MoinMoin._tests.ldap_testbase import LDAPTstBase, LdapEnvironment, check_environ, SLAPD_EXECUTABLE
+from MoinMoin._tests.ldap_testdata import *
+from MoinMoin._tests import nuke_user
+
+# first check if we have python 2.4, python-ldap and slapd:
+msg = check_environ()
+if msg:
+    py.test.skip(msg)
+del msg
+
+import ldap
+
+class TestSimpleLdap(LDAPTstBase):
+    basedn = BASEDN
+    rootdn = ROOTDN
+    rootpw = ROOTPW
+    slapd_config = SLAPD_CONFIG
+    ldif_content = LDIF_CONTENT
+
+    def testLDAP(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
+        lo = ldap.initialize(server_uri)
+        ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+        lo.simple_bind_s('', '')
+        lusers = lo.search_st(base_dn, ldap.SCOPE_SUBTREE, '(uid=*)')
+        uids = [ldap_dict['uid'][0] for dn, ldap_dict in lusers]
+        assert 'usera' in uids
+        assert 'userb' in uids
+
+    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:
+        u = handle_auth(None, username='', password='', login=True)
+        assert u is None
+        u = handle_auth(None, username='usera', password='', login=True)
+        assert u is None
+        u = handle_auth(None, username='usera', password='userawrong', login=True)
+        assert u is None
+        u = handle_auth(None, username='userawrong', password='usera', login=True)
+        assert u is None
+
+        # tests that must authenticate:
+        u1 = handle_auth(None, username='usera', password='usera', login=True)
+        assert u1 is not None
+        assert u1.valid
+
+        u2 = handle_auth(None, username='userb', password='userb', login=True)
+        assert u2 is not None
+        assert u2.valid
+
+        # check if usera and userb have different ids:
+        assert u1.id != u2.id
+
+
+class TestBugDefaultPasswd(LDAPTstBase):
+    basedn = BASEDN
+    rootdn = ROOTDN
+    rootpw = ROOTPW
+    slapd_config = SLAPD_CONFIG
+    ldif_content = LDIF_CONTENT
+
+    def teardown_class(self):
+        """ Stop slapd, remove LDAP server environment """
+        #self.ldap_env.stop_slapd()  # it is already stopped
+        self.ldap_env.destroy_env()
+
+    def testBugDefaultPasswd(self):
+        """ Login via LDAP (this creates user profile and up to 1.7.0rc1 it put
+            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')
+
+        handle_auth = self.request.handle_auth
+
+        # do a LDAPAuth login (as a side effect, this autocreates the user profile):
+        u1 = handle_auth(None, username='usera', password='usera', login=True)
+        assert u1 is not None
+        assert u1.valid
+
+        # now we kill the LDAP server:
+        self.ldap_env.slapd.stop()
+
+        # now try a MoinAuth login:
+        # try the default password that worked in 1.7 up to rc1:
+        u2 = handle_auth(None, username='usera', password='{SHA}NotStored', login=True)
+        assert u2 is None
+
+        # try using no password:
+        u2 = handle_auth(None, username='usera', password='', login=True)
+        assert u2 is None
+
+        # try using wrong password:
+        u2 = handle_auth(None, username='usera', password='wrong', login=True)
+        assert u2 is None
+
+
+class TestTwoLdapServers:
+    basedn = BASEDN
+    rootdn = ROOTDN
+    rootpw = ROOTPW
+    slapd_config = SLAPD_CONFIG
+    ldif_content = LDIF_CONTENT
+
+    def setup_class(self):
+        """ Create LDAP servers environment, start slapds """
+        self.ldap_envs = []
+        for instance in range(2):
+            ldap_env = LdapEnvironment(self.basedn, self.rootdn, self.rootpw, instance=instance)
+            ldap_env.create_env(slapd_config=self.slapd_config)
+            started = ldap_env.start_slapd()
+            if not started:
+                py.test.skip("Failed to start %s process, please see your syslog / log files"
+                             " (and check if stopping apparmor helps, in case you use it)." % SLAPD_EXECUTABLE)
+            ldap_env.load_directory(ldif_content=self.ldif_content)
+            self.ldap_envs.append(ldap_env)
+
+    def teardown_class(self):
+        """ Stop slapd, remove LDAP server environment """
+        for ldap_env in self.ldap_envs:
+            ldap_env.stop_slapd()
+            ldap_env.destroy_env()
+
+    def testLDAP(self):
+        """ Just try accessing the LDAP servers and see if usera and userb are in LDAP. """
+        for ldap_env in self.ldap_envs:
+            server_uri = ldap_env.slapd.url
+            base_dn = ldap_env.basedn
+            lo = ldap.initialize(server_uri)
+            ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+            lo.simple_bind_s('', '')
+            lusers = lo.search_st(base_dn, ldap.SCOPE_SUBTREE, '(uid=*)')
+            uids = [ldap_dict['uid'][0] for dn, ldap_dict in lusers]
+            assert 'usera' in uids
+            assert 'userb' in uids
+
+
+class TestLdapFailover:
+    basedn = BASEDN
+    rootdn = ROOTDN
+    rootpw = ROOTPW
+    slapd_config = SLAPD_CONFIG
+    ldif_content = LDIF_CONTENT
+
+    def setup_class(self):
+        """ Create LDAP servers environment, start slapds """
+        self.ldap_envs = []
+        for instance in range(2):
+            ldap_env = LdapEnvironment(self.basedn, self.rootdn, self.rootpw, instance=instance)
+            ldap_env.create_env(slapd_config=self.slapd_config)
+            started = ldap_env.start_slapd()
+            if not started:
+                py.test.skip("Failed to start %s process, please see your syslog / log files"
+                             " (and check if stopping apparmor helps, in case you use it)." % SLAPD_EXECUTABLE)
+            ldap_env.load_directory(ldif_content=self.ldif_content)
+            self.ldap_envs.append(ldap_env)
+
+    def teardown_class(self):
+        """ Stop slapd, remove LDAP server environment """
+        for ldap_env in self.ldap_envs:
+            try:
+                ldap_env.stop_slapd()
+            except:
+                pass # one will fail, because it is already stopped
+            ldap_env.destroy_env()
+
+    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):
+        u1 = handle_auth(None, username='usera', password='usera', login=True)
+        assert u1 is not None
+        assert u1.valid
+
+        # now we kill our primary LDAP server:
+        self.ldap_envs[0].slapd.stop()
+
+        # try if we can still authenticate (with the second slapd):
+        u2 = handle_auth(None, username='usera', password='usera', login=True)
+        assert u2 is not None
+        assert u2.valid
+
+
+
--- a/MoinMoin/auth/ldap_login.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/auth/ldap_login.py	Thu Jul 17 16:29:23 2008 +0900
@@ -17,11 +17,15 @@
                 2006 Nick Phillips
     @license: GNU GPL, see COPYING for details.
 """
-import ldap
-
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
+try:
+    import ldap
+except ImportError, err:
+    logging.error("You need to have python-ldap installed (%s)." % str(err))
+    raise
+
 from MoinMoin import user
 from MoinMoin.auth import BaseAuth, CancelLogin, ContinueLogin
 
@@ -180,10 +184,10 @@
                 result_length = len(lusers)
                 if result_length != 1:
                     if result_length > 1:
-                        logging.debug("Search found more than one (%d) matches for %r." % (result_length, filterstr))
+                        logging.warning("Search found more than one (%d) matches for %r." % (result_length, filterstr))
                     if result_length == 0:
                         logging.debug("Search found no matches for %r." % (filterstr, ))
-                    return CancelLogin(_("Invalid username or password."))
+                    return ContinueLogin(user_obj, _("Invalid username or password."))
 
                 dn, ldap_dict = lusers[0]
                 if not self.bind_once:
@@ -214,10 +218,10 @@
                 aliasname = aliasname.decode(coding)
 
                 if email:
-                    u = user.User(request, auth_username=username, password="{SHA}NotStored", auth_method=self.name, auth_attribs=('name', 'password', 'email', 'mailto_author', ))
+                    u = user.User(request, auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'email', 'mailto_author', ))
                     u.email = email
                 else:
-                    u = user.User(request, auth_username=username, password="{SHA}NotStored", auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author', ))
+                    u = user.User(request, auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author', ))
                 u.name = username
                 u.aliasname = aliasname
                 u.remember_me = 0 # 0 enforces cookie_lifetime config param
@@ -231,7 +235,16 @@
                 u.create_or_update(True)
             return ContinueLogin(u)
 
+        except ldap.SERVER_DOWN, err:
+            # looks like this LDAP server isn't working, so we just try the next
+            # authenticator object in cfg.auth list (there could be some second
+            # ldap authenticator that queries a backup server or any other auth
+            # method).
+            logging.error("LDAP server %s failed (%s). "
+                          "Trying to authenticate with next auth list entry." % (server, str(err)))
+            return ContinueLogin(user_obj, _("LDAP server %(server)s failed.") % {'server': server})
+
         except:
             logging.exception("caught an exception, traceback follows...")
-            return CancelLogin(None)
+            return ContinueLogin(user_obj)
 
--- a/MoinMoin/auth/openidrp.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/auth/openidrp.py	Thu Jul 17 16:29:23 2008 +0900
@@ -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']
 
@@ -93,6 +95,11 @@
                               value=_('Choose this name')))
         table.append(html.TR().append(td1).append(td2))
 
+    def _get_account_name_inval_user(self, request, form):
+        _ = request.getText
+        msg = _('This is not a valid username, choose a different one.')
+        return self._get_account_name(request, form, msg=msg)
+
     def _associate_account(self, request, form, accountname, msg=None):
         _ = request.getText
 
@@ -130,9 +137,9 @@
         query = {}
         for key in request.form:
             query[key] = request.form[key][0]
-        return_to = get_multistage_continuation_url(request, self.name,
-                                                    {'oidstage': '1'})
-        info = oidconsumer.complete(query, return_to=return_to)
+        current_url = get_multistage_continuation_url(request, self.name,
+                                                      {'oidstage': '1'})
+        info = oidconsumer.complete(query, current_url)
         if info.status == consumer.FAILURE:
             return CancelLogin(_('OpenID error: %s.') % info.message)
         elif info.status == consumer.CANCEL:
@@ -170,8 +177,7 @@
         if not newname:
             return MultistageFormLogin(self._get_account_name)
         if not user.isValidName(request, newname):
-            return MultistageFormLogin(self._get_account_name,
-                    _('This is not a valid username, choose a different one.'))
+            return MultistageFormLogin(self._get_account_name_inval_user)
         uid = None
         if newname:
             uid = user.getUserId(request, newname)
@@ -290,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/caching.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/caching.py	Thu Jul 17 16:29:23 2008 +0900
@@ -3,11 +3,13 @@
     MoinMoin caching module
 
     @copyright: 2001-2004 by Juergen Hermann <jh@web.de>,
-                2006-2008 MoinMoin:ThomasWaldmann
+                2006-2008 MoinMoin:ThomasWaldmann,
+                2008 MoinMoin:ThomasPfaff
     @license: GNU GPL, see COPYING for details.
 """
 
 import os
+import shutil
 import tempfile
 
 from MoinMoin import log
@@ -21,6 +23,7 @@
     """ raised if we have trouble reading or writing to the cache """
     pass
 
+
 def get_arena_dir(request, arena, scope):
     if scope == 'page_or_wiki': # XXX DEPRECATED, remove later
         if isinstance(arena, str):
@@ -69,22 +72,38 @@
         self.arena_dir = get_arena_dir(request, arena, scope)
         if not os.path.exists(self.arena_dir):
             os.makedirs(self.arena_dir)
+        self._fname = os.path.join(self.arena_dir, key)
+
         if self.locking:
             self.lock_dir = os.path.join(self.arena_dir, '__lock__')
             self.rlock = lock.LazyReadLock(self.lock_dir, 60.0)
             self.wlock = lock.LazyWriteLock(self.lock_dir, 60.0)
 
+        # used by file-like api:
+        self._lock = None  # either self.rlock or self.wlock
+        self._fileobj = None  # open cache file object
+        self._tmp_fname = None  # name of temporary file (used for write)
+        self._mode = None  # mode of open file object
+
+
     def _filename(self):
-        return os.path.join(self.arena_dir, self.key)
+        # DEPRECATED - please use file-like api
+        return self._fname
 
     def exists(self):
-        return os.path.exists(self._filename())
+        return os.path.exists(self._fname)
 
     def mtime(self):
         # DEPRECATED for checking a changed on-disk cache, please use
         # self.uid() for this, see below
         try:
-            return os.path.getmtime(self._filename())
+            return os.path.getmtime(self._fname)
+        except (IOError, OSError):
+            return 0
+
+    def size(self):
+        try:
+            return os.path.getsize(self._fname)
         except (IOError, OSError):
             return 0
 
@@ -93,7 +112,7 @@
 
             See docstring of MoinMoin.util.filesys.fuid for details.
         """
-        return filesys.fuid(self._filename())
+        return filesys.fuid(self._fname)
 
     def needsUpdate(self, filename, attachdir=None):
         # following code is not necessary. will trigger exception and give same result
@@ -101,7 +120,7 @@
         #    return 1
 
         try:
-            ctime = os.path.getmtime(self._filename())
+            ctime = os.path.getmtime(self._fname)
             ftime = os.path.getmtime(filename)
         except os.error:
             return 1
@@ -118,53 +137,137 @@
 
         return needsupdate
 
-#    def copyto(self, filename):
-#        # currently unused function
-#        import shutil
-#        tmpfname = self._tmpfilename()
-#        fname = self._filename()
-#        if not self.locking or self.locking and self.wlock.acquire(1.0):
-#            try:
-#                shutil.copyfile(filename, tmpfname)
-#                # this is either atomic or happening with real locks set:
-#                filesys.rename(tmpfname, fname)
-#            finally:
-#                if self.locking:
-#                    self.wlock.release()
-#        else:
-#            logging.error("Can't acquire write lock in %s" % self.lock_dir)
+    def _determine_locktype(self, mode):
+        """ return the correct lock object for a specific file access mode """
+        if self.locking:
+            if 'r' in mode:
+                lock = self.rlock
+            if 'w' in mode or 'a' in mode:
+                lock = self.wlock
+        else:
+            lock = None
+        return lock
+
+    # file-like interface ----------------------------------------------------
+
+    def open(self, filename=None, mode='r', bufsize=-1):
+        """ open the cache for reading/writing
+
+        @param filename: must be None (default - automatically determine filename)
+        @param mode: 'r' (read, default), 'w' (write)
+                     Note: if mode does not include 'b' (binary), it will be
+                           automatically changed to include 'b'.
+        @param bufsize: size of read/write buffer (default: -1 meaning automatic)
+        @return: None (the opened file object is kept in self._fileobj and used
+                 implicitely by read/write/close functions of CacheEntry object.
+        """
+        assert self._fileobj is None, 'caching: trying to open an already opened cache'
+        assert filename is None, 'caching: giving a filename is not supported (yet?)'
+
+        self._lock = self._determine_locktype(mode)
+
+        if 'b' not in mode:
+            mode += 'b'  # we want to use binary mode, ever!
+        self._mode = mode  # for self.close()
+
+        if not self.locking or self.locking and self._lock.acquire(1.0):
+            try:
+                if 'r' in mode:
+                    self._fileobj = open(self._fname, mode, bufsize)
+                elif 'w' in mode:
+                    # we do not write content to old inode, but to a new file
+                    # so we don't need to lock when we just want to read the file
+                    # (at least on POSIX, this works)
+                    fd, self._tmp_fname = tempfile.mkstemp('.tmp', self.key, self.arena_dir)
+                    self._fileobj = os.fdopen(fd, mode, bufsize)
+                else:
+                    raise ValueError("caching: mode does not contain 'r' or 'w'")
+            finally:
+                if self.locking:
+                    self._lock.release()
+                    self._lock = None
+        else:
+            logging.error("Can't acquire read/write lock in %s" % self.lock_dir)
+
+
+    def read(self, size=-1):
+        """ read data from cache file
+
+        @param size: how many bytes to read (default: -1 == everything)
+        @return: read data (str)
+        """
+        return self._fileobj.read(size)
+
+    def write(self, data):
+        """ write data to cache file
+
+        @param data: write data (str)
+        """
+        self._fileobj.write(data)
+
+    def close(self):
+        """ close cache file (and release lock, if any) """
+        if self._fileobj:
+            self._fileobj.close()
+            self._fileobj = None
+            if 'w' in self._mode:
+                filesys.chmod(self._tmp_fname, 0666 & config.umask) # fix mode that mkstemp chose
+                # this is either atomic or happening with real locks set:
+                filesys.rename(self._tmp_fname, self._fname)
+
+        if self._lock:
+            if self.locking:
+                self._lock.release()
+            self._lock = None
+
+    # ------------------------------------------------------------------------
 
     def update(self, content):
         try:
-            fname = self._filename()
-            if self.use_pickle:
-                content = pickle.dumps(content, PICKLE_PROTOCOL)
-            elif self.use_encode:
-                content = content.encode(config.charset)
-            if not self.locking or self.locking and self.wlock.acquire(1.0):
+            if hasattr(content, 'read'):
+                # content is file-like
+                assert not (self.use_pickle or self.use_encode), 'caching: use_pickle and use_encode not supported with file-like api'
                 try:
-                    # we do not write content to old inode, but to a new file
-                    # so we don't need to lock when we just want to read the file
-                    # (at least on POSIX, this works)
-                    tmp_handle, tmp_fname = tempfile.mkstemp('.tmp', self.key, self.arena_dir)
-                    os.write(tmp_handle, content)
-                    os.close(tmp_handle)
-                    # this is either atomic or happening with real locks set:
-                    filesys.rename(tmp_fname, fname)
-                    filesys.chmod(fname, 0666 & config.umask) # fix mode that mkstemp chose
+                    self.open(mode='w')
+                    shutil.copyfileobj(content, self)
                 finally:
-                    if self.locking:
-                        self.wlock.release()
+                    self.close()
             else:
-                logging.error("Can't acquire write lock in %s" % self.lock_dir)
+                # content is a string
+                if self.use_pickle:
+                    content = pickle.dumps(content, PICKLE_PROTOCOL)
+                elif self.use_encode:
+                    content = content.encode(config.charset)
+
+                try:
+                    self.open(mode='w')
+                    self.write(content)
+                finally:
+                    self.close()
         except (pickle.PicklingError, OSError, IOError, ValueError), err:
             raise CacheError(str(err))
 
+    def content(self):
+        # no file-like api yet, we implement it when we need it
+        try:
+            try:
+                self.open(mode='r')
+                data = self.read()
+            finally:
+                self.close()
+            if self.use_pickle:
+                data = pickle.loads(data)
+            elif self.use_encode:
+                data = data.decode(config.charset)
+            return data
+        except (pickle.UnpicklingError, IOError, EOFError, ValueError), err:
+            raise CacheError(str(err))
+
     def remove(self):
         if not self.locking or self.locking and self.wlock.acquire(1.0):
             try:
                 try:
-                    os.remove(self._filename())
+                    os.remove(self._fname)
                 except OSError:
                     pass
             finally:
@@ -173,23 +276,4 @@
         else:
             logging.error("Can't acquire write lock in %s" % self.lock_dir)
 
-    def content(self):
-        try:
-            if not self.locking or self.locking and self.rlock.acquire(1.0):
-                try:
-                    f = open(self._filename(), 'rb')
-                    data = f.read()
-                    f.close()
-                finally:
-                    if self.locking:
-                        self.rlock.release()
-            else:
-                logging.error("Can't acquire read lock in %s" % self.lock_dir)
-            if self.use_pickle:
-                data = pickle.loads(data)
-            elif self.use_encode:
-                data = data.decode(config.charset)
-            return data
-        except (pickle.UnpicklingError, IOError, EOFError, ValueError), err:
-            raise CacheError(str(err))
 
--- a/MoinMoin/config/multiconfig.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/config/multiconfig.py	Thu Jul 17 16:29:23 2008 +0900
@@ -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,499 +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'] # ['DeletePage', 'AttachFile', 'RenamePage', 'test', ]
-    allow_xslt = False
-    antispam_master_url = "http://master.moinmo.in/?action=xmlrpc2"
-
-    auth = [authmodule.MoinLogin()]
-    # 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
-
-    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>\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 = 10
-
-    session_handler = session.DefaultSessionHandler()
-    session_id_handler = session.MoinCookieSessionIDHandler()
-
-    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),
-        'show': (30, 60),
-        'recall': (10, 120),
-        'raw': (20, 40),  # some people use this for css
-        'AttachFile': (90, 60),
-        'diff': (30, 60),
-        'fullsearch': (10, 120),
-        'edit': (30, 300), # can be lowered after making preview different from edit
-        'rss_rc': (1, 60),
-        'default': (30, 60),
-    }
-    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 = '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 """
@@ -751,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()
 
@@ -787,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:
@@ -808,6 +338,7 @@
 
         # 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
@@ -832,9 +363,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]
@@ -842,10 +370,10 @@
         if self.url_prefix_local is None:
             self.url_prefix_local = self.url_prefix_static
 
-
+    _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)
@@ -860,25 +388,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
@@ -943,13 +459,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', 'logo_string', 'navi_bar', 'page_front_page',
@@ -996,7 +512,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.
 
@@ -1006,50 +522,58 @@
 
 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
+        import imp, sha
 
-        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' % sha.new(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)
+                            self._plugin_modules.append(modname)
+                        finally:
+                            # Make sure fp is closed properly
+                            if fp:
+                                fp.close()
             finally:
                 imp.release_lock()
         except ImportError, err:
-            msg = '''
-Could not import plugin package "%(path)s/plugin" because of ImportError:
+            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)
@@ -1069,6 +593,580 @@
         """ 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(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 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)
+
+
+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_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.'),
+    ('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', None, (
+    ('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),
+        'show': (30, 60),
+        'recall': (10, 120),
+        'raw': (20, 40),  # some people use this for css
+        'AttachFile': (90, 60),
+        'diff': (30, 60),
+        'fullsearch': (10, 120),
+        'edit': (30, 300), # can be lowered after making preview different from edit
+        'rss_rc': (1, 60),
+        'default': (30, 60),
+     },
+     "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. number of files which are extracted from the zip file"),
+    ('unzip_attachments_space', 200.0 * 1000 ** 2,
+     "max. total amount of bytes can be used to unzip files [bytes]"),
+    ('unzip_attachments_count', 101,
+     "max. size of a single file in the archive which will be extracted [bytes]"),
+  )),
+  # ==========================================================================
+  'style': ('Style / Theme / UI related', None, (
+    ('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, 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]"`. To use page names with spaces, use `u"[page_name_with_spaces any 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_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_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', None, (
+      ('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, (
+      ('autocreate', False,
+       "if True, user accounts are created automatically (see HelpOnAuthentication)."),
+      ('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', 'Self',
+       "interwiki name of the wiki where the user home pages are located (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_secret', "", "Shared secret for mail importing"),
+      ('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"),
+    )),
+}
+
+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/events/_tests/test_events.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/events/_tests/test_events.py	Thu Jul 17 16:29:23 2008 +0900
@@ -49,6 +49,7 @@
                    "StupidType", request, page, "en", revisions=page.getRevList())
 
 def test_user_created_event(request):
+    py.test.skip("Test is wrong, because it assumes send_notification will be called - but if there is no superuser subscribed to UserCreatedEvent, then no notification needs to be sent.")
     class server_dummy:
         def __init__(self):
             self.sent = False
--- a/MoinMoin/failure.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/failure.py	Thu Jul 17 16:29:23 2008 +0900
@@ -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/filter/__init__.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/filter/__init__.py	Thu Jul 17 16:29:23 2008 +0900
@@ -2,11 +2,15 @@
 """
     MoinMoin - Filter Package
 
-    @copyright: 2006 Thomas Waldmann
+    @copyright: 2006-2008 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
 import os
+
+from MoinMoin import log
+logging = log.getLogger(__name__)
+
 from MoinMoin.util import pysupport
 
 modules = pysupport.getPackageModules(__file__)
@@ -18,9 +22,13 @@
         to decode to unicode, we use the first coding of codings list that
         does not throw an exception or force ascii
     """
-    f = os.popen(cmd % filename)
-    data = f.read()
-    f.close()
+    filter_cmd = cmd % filename
+    child_stdin, child_stdout, child_stderr = os.popen3(filter_cmd)
+    data = child_stdout.read()
+    errors = child_stderr.read()
+    child_stdout.close()
+    child_stderr.close()
+    logging.debug("cmd: %s stderr: %s" % (filter_cmd, errors))
     for c in codings:
         try:
             return data.decode(c)
--- a/MoinMoin/i18n/MoinMoin.pot	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/MoinMoin.pot	Thu Jul 17 16:29:23 2008 +0900
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-22 22:32+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\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"
@@ -434,6 +434,9 @@
 msgid "filter"
 msgstr ""
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -519,6 +522,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr ""
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 msgid "You need to log in."
 msgstr ""
 
@@ -537,6 +544,9 @@
 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"
@@ -557,9 +567,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1109,6 +1116,15 @@
 msgid "Quick links"
 msgstr ""
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1215,14 +1231,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 msgid "Attachment link"
 msgstr ""
 
@@ -1545,30 +1553,18 @@
 msgid "index unavailable"
 msgstr ""
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr ""
+
 msgid "N/A"
 msgstr ""
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr ""
-
 msgid "Xapian search"
 msgstr ""
 
-msgid "Xapian Version"
-msgstr ""
-
-msgid "PyStemmer not installed"
-msgstr ""
-
 msgid "Stemming for Xapian"
 msgstr ""
 
-msgid "PyStemmer Version"
-msgstr ""
-
-msgid "PyStemmer stems"
-msgstr ""
-
 msgid "Active threads"
 msgstr ""
 
@@ -2042,9 +2038,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2575,15 +2571,32 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+msgid "The file %(filename)s is not a .zip file."
 msgstr ""
 
 #, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
 msgstr ""
 
 #, python-format
@@ -2591,16 +2604,6 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr ""
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr ""
 
--- a/MoinMoin/i18n/__init__.py	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/__init__.py	Thu Jul 17 16:29:23 2008 +0900
@@ -290,7 +290,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 Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/ar.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language ar
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: moin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2008-04-12 20:07+0200\n"
 "Last-Translator: Mohamed Yahya <yahya.mohamed@gmail.com>\n"
 "Language-Team: \n"
@@ -453,6 +453,9 @@
 msgid "filter"
 msgstr "مرشح"
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -552,6 +555,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr "لا يوجد كلمة سر. الرجاء إدخال إسم مستخدم و كلمة سر"
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 #, fuzzy
 msgid "You need to log in."
 msgstr "لقد قمت بتسجيل الخروج."
@@ -571,6 +578,9 @@
 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"
@@ -592,9 +602,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1163,6 +1170,15 @@
 msgid "Quick links"
 msgstr "روابط سريعة"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1271,14 +1287,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "المرفقات"
@@ -1615,31 +1623,19 @@
 msgid "index unavailable"
 msgstr ""
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr ""
+
 #, fuzzy
 msgid "N/A"
 msgstr "غير متاح"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr ""
-
 msgid "Xapian search"
 msgstr ""
 
-msgid "Xapian Version"
-msgstr ""
-
-msgid "PyStemmer not installed"
-msgstr ""
-
 msgid "Stemming for Xapian"
 msgstr ""
 
-msgid "PyStemmer Version"
-msgstr ""
-
-msgid "PyStemmer stems"
-msgstr ""
-
 msgid "Active threads"
 msgstr ""
 
@@ -1712,6 +1708,12 @@
 msgstr ""
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr ""
 
@@ -2121,9 +2123,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2378,12 +2380,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2692,15 +2688,32 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+msgid "The file %(filename)s is not a .zip file."
 msgstr ""
 
 #, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
 msgstr ""
 
 #, python-format
@@ -2708,16 +2721,6 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr ""
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr ""
 
--- a/MoinMoin/i18n/bg.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/bg.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language bg
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2006-02-11 12:20-0800\n"
 "Last-Translator: Hristo Iliev <hristo@phys.uni-sofia.bg>\n"
 "Language-Team: Bulgarian <moin-devel@lists.sourceforge.net>\n"
@@ -492,6 +492,9 @@
 msgid "filter"
 msgstr "филтър"
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -586,6 +589,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr "Липсва парола. Моля, въведете потребителско име и парола."
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 #, fuzzy
 msgid "You need to log in."
 msgstr "Вие излязохте успешно."
@@ -605,6 +612,9 @@
 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"
@@ -626,9 +636,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1194,6 +1201,15 @@
 msgid "Quick links"
 msgstr "Бързи връзки"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1329,14 +1345,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "Приложения"
@@ -1723,32 +1731,18 @@
 msgid "index unavailable"
 msgstr "недостъпен индекс"
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "Xapian и/или Python Xapian не са инсталирани"
+
 msgid "N/A"
 msgstr "няма"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "Xapian и/или Python Xapian не са инсталирани"
-
 msgid "Xapian search"
 msgstr "Търсене чрез Xapian"
 
-msgid "Xapian Version"
-msgstr "Версия на Xapian"
-
-msgid "PyStemmer not installed"
-msgstr ""
-
 msgid "Stemming for Xapian"
 msgstr ""
 
-#, fuzzy
-msgid "PyStemmer Version"
-msgstr "Версия на Python"
-
-#, fuzzy
-msgid "PyStemmer stems"
-msgstr "Версия на Python"
-
 msgid "Active threads"
 msgstr "Активни нишки"
 
@@ -1821,6 +1815,14 @@
 msgstr "Моля използвайте по-избирателни термини за търсене вместо {{{\"%s\"}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+"Заявката за търсене {{{\"%s\"}}} е невалидна. Моля, погледнете "
+"HelpOnSearching за повече информация."
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "Качване на ново приложение \"%(filename)s\""
 
@@ -2254,7 +2256,7 @@
 msgstr ""
 "Моля, създайте Ваша домашна страница преди да започнете да създавате други."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
 "You can add some additional sub pages to your already existing homepage "
 "here.\n"
@@ -2274,9 +2276,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2563,14 +2565,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-"Заявката за търсене {{{\"%s\"}}} е невалидна. Моля, погледнете "
-"HelpOnSearching за повече информация."
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2895,38 +2889,45 @@
 msgstr "Не Ви е позволено да разархивирате допълнения от тази страница."
 
 #, python-format
+msgid "The file %(filename)s is not a .zip file."
+msgstr "Файлът %(filename)s не е .zip файл."
+
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+"Приложение '%(filename)s' не може да бъде разархивирано, защото файловете са "
+"твъде големи, само .zip са, вече съществуват или се намират в папки."
+
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
 msgstr ""
 "Приложение '%(filename)s' не може да бъде разархивирано, защото файловете ще "
 "станат твърде дълги (липсват %(space)d kB)."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
 msgstr ""
 "Приложение '%(filename)s' не може да бъде разархивирано, защото файловете ще "
 "станат твърде много (липсват %(count)d)."
 
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr "Приложение '%(filename)s' - разархивирано."
+
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "Приложение '%(filename)s' - разархивирано."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-"Приложение '%(filename)s' не може да бъде разархивирано, защото файловете са "
-"твъде големи, само .zip са, вече съществуват или се намират в папки."
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "Файлът %(filename)s не е .zip файл."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "Приложение '%(filename)s'"
 
@@ -2999,6 +3000,17 @@
 msgid "Login and try again."
 msgstr "Влезте и опитайте отново."
 
+#~ msgid "Xapian Version"
+#~ msgstr "Версия на Xapian"
+
+#, fuzzy
+#~ msgid "PyStemmer Version"
+#~ msgstr "Версия на Python"
+
+#, fuzzy
+#~ msgid "PyStemmer stems"
+#~ msgstr "Версия на Python"
+
 #, fuzzy
 #~ msgid "New Page or New Attachment"
 #~ msgstr "Ново приложение"
--- a/MoinMoin/i18n/ca.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/ca.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language ca
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: moin 0.5\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2007-10-10 13:40+0200\n"
 "Last-Translator: Jordi Mallach <jordi@sindominio.net>\n"
 "Language-Team: Catalan <ca@dodds.net>\n"
@@ -483,6 +483,9 @@
 msgid "filter"
 msgstr "filtre"
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -575,6 +578,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr "Manca la contrasenya. Introduïu el nom d'usuari i contrasenya."
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 #, fuzzy
 msgid "You need to log in."
 msgstr "Heu sortit del vostre compte."
@@ -594,6 +601,9 @@
 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"
@@ -615,9 +625,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1179,6 +1186,15 @@
 msgid "Quick links"
 msgstr "Enllaços ràpids"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1315,14 +1331,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "Adjuncions"
@@ -1710,30 +1718,18 @@
 msgid "index unavailable"
 msgstr ""
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "El Xapian i les vinculacions Xapian de Python no estan instal·lades"
+
 msgid "N/A"
 msgstr "N/D"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "El Xapian i les vinculacions Xapian de Python no estan instal·lades"
-
 msgid "Xapian search"
 msgstr "Cerca Xapian"
 
-msgid "Xapian Version"
-msgstr "Versió del Xapian"
-
-msgid "PyStemmer not installed"
-msgstr "El PyStemmer no està instal·lat"
-
 msgid "Stemming for Xapian"
 msgstr "Lexemització per a Xapian"
 
-msgid "PyStemmer Version"
-msgstr "Versió de PyStemmer"
-
-msgid "PyStemmer stems"
-msgstr "Lexemes de PyStemmer"
-
 msgid "Active threads"
 msgstr "Fils actius"
 
@@ -1806,6 +1802,12 @@
 msgstr "Utilitzeu un terme més selectiu en comptes de {{{«%s»}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "Apuja una nova adjunció «%(filename)s»"
 
@@ -2207,9 +2209,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2465,12 +2467,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2793,38 +2789,45 @@
 msgstr "No teniu permís per a decomprimir les adjuncions d'aquesta pàgina."
 
 #, python-format
+msgid "The file %(filename)s is not a .zip file."
+msgstr "El fitxer %(filename)s no és un fitxer .zip."
+
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+"No s'ha desempaquetat l'adjunció «%(filename)s» perquè els fitxers són massa "
+"grans, només fitxers .zip, ja existeixen o estan dins de carpetes."
+
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
 msgstr ""
 "L'adjunció «%(filename)s» no s'ha pogut desempaquetar perquè els fitxers "
 "resultants serien massa grans (falten %(space)d kB)."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
 msgstr ""
 "L'adjunció «%(filename)s» no s'ha pogut desempaquetar perquè hi hauria massa "
 "fitxers resultants (falten %(count)d)."
 
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr "S'ha desempaquetat l'adjunció «%(filename)s»."
+
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "S'ha desempaquetat l'adjunció «%(filename)s»."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-"No s'ha desempaquetat l'adjunció «%(filename)s» perquè els fitxers són massa "
-"grans, només fitxers .zip, ja existeixen o estan dins de carpetes."
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "El fitxer %(filename)s no és un fitxer .zip."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "Adjunció «%(filename)s»"
 
@@ -2899,6 +2902,18 @@
 msgid "Login and try again."
 msgstr "Entreu i torneu a provar."
 
+#~ msgid "Xapian Version"
+#~ msgstr "Versió del Xapian"
+
+#~ msgid "PyStemmer not installed"
+#~ msgstr "El PyStemmer no està instal·lat"
+
+#~ msgid "PyStemmer Version"
+#~ msgstr "Versió de PyStemmer"
+
+#~ msgid "PyStemmer stems"
+#~ msgstr "Lexemes de PyStemmer"
+
 #~ msgid "New Page or New Attachment"
 #~ msgstr "Pàgina o adjunció nova"
 
--- a/MoinMoin/i18n/cs.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/cs.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language cs
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2007-11-05 22:11+0100\n"
 "Last-Translator: Václav Haisman <v.haisman@sh.cvut.cz>\n"
 "Language-Team: Czech <moin@lists.sourceforge.net>\n"
@@ -484,6 +484,9 @@
 msgid "filter"
 msgstr "filtr"
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -573,6 +576,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr "Prázdné heslo. Prosím vložte jméno a heslo."
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 #, fuzzy
 msgid "You need to log in."
 msgstr "Jste nyní odhlášeni."
@@ -592,6 +599,9 @@
 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"
@@ -613,9 +623,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1179,6 +1186,15 @@
 msgid "Quick links"
 msgstr "Pohotové odkazy"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1316,14 +1332,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "Přílohy"
@@ -1708,30 +1716,18 @@
 msgid "index unavailable"
 msgstr "index není dostupný"
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "Xapian a/nebo Python Xapian bindings nenainstalováno"
+
 msgid "N/A"
 msgstr "N/A"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "Xapian a/nebo Python Xapian bindings nenainstalováno"
-
 msgid "Xapian search"
 msgstr "Xapian hledání"
 
-msgid "Xapian Version"
-msgstr "Xapian Verze"
-
-msgid "PyStemmer not installed"
-msgstr "PyStemmer není nainstalován"
-
 msgid "Stemming for Xapian"
 msgstr "Hledání slovních základů pro Xapian"
 
-msgid "PyStemmer Version"
-msgstr "PyStemmer Verze"
-
-msgid "PyStemmer stems"
-msgstr "Lematizátory PyStemmeru"
-
 msgid "Active threads"
 msgstr "Aktivní vlákna"
 
@@ -1804,6 +1800,13 @@
 msgstr "Použijte prosím selektivnější vyhledávací výraz místo {{{\"%s\"}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+"Váš dotaz {{{\"%s\"}}} je neplatný. Více informací viz HelpOnSearching."
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "Upload nové přílohy \"%(filename)s\""
 
@@ -2229,7 +2232,7 @@
 msgid "Please first create a homepage before creating additional pages."
 msgstr "Prosím, nejdříve vytvořte domovskou stránku."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
 "You can add some additional sub pages to your already existing homepage "
 "here.\n"
@@ -2249,9 +2252,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2525,13 +2528,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-"Váš dotaz {{{\"%s\"}}} je neplatný. Více informací viz HelpOnSearching."
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2850,38 +2846,45 @@
 msgstr "Nemáte dovoleno rozbalovat přílohy na této stránce."
 
 #, python-format
+msgid "The file %(filename)s is not a .zip file."
+msgstr "Soubor %(filename)s není .zip archív."
+
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+"Příloha '%(filename)s' nerozbalena. Soubory jsou buď příliš velké, nebo jsou "
+"to pouze .zip soubory, nebo už existují, nebo jsou v adresářích."
+
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
 msgstr ""
 "Příloha '%(filename)s' nemohla být rozbalena kvůli nedostatku místa (schází %"
 "(space)d kB."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
 msgstr ""
 "Příloha '%(filename)s' nemohla být rozbalena kvůli příliš velkému počtu "
 "souborů (schází %(count)d)."
 
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr "Příloha '%(filename)s' rozbalena."
+
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "Příloha '%(filename)s' rozbalena."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-"Příloha '%(filename)s' nerozbalena. Soubory jsou buď příliš velké, nebo jsou "
-"to pouze .zip soubory, nebo už existují, nebo jsou v adresářích."
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "Soubor %(filename)s není .zip archív."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "Příloha '%(filename)s'"
 
@@ -2953,6 +2956,18 @@
 msgid "Login and try again."
 msgstr "Přihlašte se a zkuste to znovu."
 
+#~ msgid "Xapian Version"
+#~ msgstr "Xapian Verze"
+
+#~ msgid "PyStemmer not installed"
+#~ msgstr "PyStemmer není nainstalován"
+
+#~ msgid "PyStemmer Version"
+#~ msgstr "PyStemmer Verze"
+
+#~ msgid "PyStemmer stems"
+#~ msgstr "Lematizátory PyStemmeru"
+
 #~ msgid "New Page or New Attachment"
 #~ msgstr "Nová Stránka nebo Nová Příloha"
 
--- a/MoinMoin/i18n/da.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/da.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language da
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2007-04-22 18:50+0100\n"
 "Last-Translator: Jonas Smedegaard <dr@jones.dk>\n"
 "Language-Team: Danish <moin-devel@lists.sourceforge.net>\n"
@@ -487,6 +487,9 @@
 msgid "filter"
 msgstr "filter"
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -581,6 +584,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr "Manglende adgangskode. Angiv venligst brugernavn og adgangskode."
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 #, fuzzy
 msgid "You need to log in."
 msgstr "Du er nu logget ud."
@@ -600,6 +607,9 @@
 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"
@@ -621,9 +631,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1187,6 +1194,15 @@
 msgid "Quick links"
 msgstr "Genveje"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1323,14 +1339,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "Vedhæftninger"
@@ -1716,32 +1724,18 @@
 msgid "index unavailable"
 msgstr "oversigt ikke tilgængelig"
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "Xapian og/eller Python Xapian bindinger ikke installeret"
+
 msgid "N/A"
 msgstr "utilgængelig"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "Xapian og/eller Python Xapian bindinger ikke installeret"
-
 msgid "Xapian search"
 msgstr "Xapian søgning"
 
-msgid "Xapian Version"
-msgstr "Xapian version"
-
-msgid "PyStemmer not installed"
-msgstr ""
-
 msgid "Stemming for Xapian"
 msgstr ""
 
-#, fuzzy
-msgid "PyStemmer Version"
-msgstr "Python-version"
-
-#, fuzzy
-msgid "PyStemmer stems"
-msgstr "Python-version"
-
 msgid "Active threads"
 msgstr "Aktive tråde"
 
@@ -1814,6 +1808,14 @@
 msgstr "Brug venligst mere præcise søgekriterier end {{{\"%s\"}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+"Din søgningsforespørgsel {{{\"%s\"}}} er forkert. Se venligst "
+"HjælpTilSøgning for yderligere information."
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "Overfør ny vedhæftning \"%(filename)s\""
 
@@ -2245,7 +2247,7 @@
 msgid "Please first create a homepage before creating additional pages."
 msgstr "Opret venligst en hjemmeside før du opretter flere sider"
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
 "You can add some additional sub pages to your already existing homepage "
 "here.\n"
@@ -2265,9 +2267,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2550,14 +2552,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-"Din søgningsforespørgsel {{{\"%s\"}}} er forkert. Se venligst "
-"HjælpTilSøgning for yderligere information."
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2883,38 +2877,45 @@
 msgstr "Du har ikke lov til at udpakke vedhæftninger til denne side."
 
 #, python-format
+msgid "The file %(filename)s is not a .zip file."
+msgstr "Filen %(filename)s er ikke en zip-fil."
+
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+"Vedhæftning '%(filename)s' ikke udpakket fordi indholdet ville fylde for "
+"meget, kun er zip-filer, eksisterer allerede eller ligger i mapper."
+
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
 msgstr ""
 "Vedhæftning '%(filename)s' kunne ikke udpakkes fordi indholdet ville fylde "
 "for meget (%(space)d kB mangler)."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
 msgstr ""
 "Vedhæftning '%(filename)s' kunne ikke udpakkes fordi den indeholder for "
 "mange filer (%(count)d for mange)."
 
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr "Vedhæftning '%(filename)s' udpakket."
+
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "Vedhæftning '%(filename)s' udpakket."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-"Vedhæftning '%(filename)s' ikke udpakket fordi indholdet ville fylde for "
-"meget, kun er zip-filer, eksisterer allerede eller ligger i mapper."
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "Filen %(filename)s er ikke en zip-fil."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "Vedhæftning '%(filename)s'"
 
@@ -2986,6 +2987,17 @@
 msgid "Login and try again."
 msgstr "Log på og prøv igen."
 
+#~ msgid "Xapian Version"
+#~ msgstr "Xapian version"
+
+#, fuzzy
+#~ msgid "PyStemmer Version"
+#~ msgstr "Python-version"
+
+#, fuzzy
+#~ msgid "PyStemmer stems"
+#~ msgstr "Python-version"
+
 #, fuzzy
 #~ msgid "New Page or New Attachment"
 #~ msgstr "Ny vedhæftning"
--- a/MoinMoin/i18n/de.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/de.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language de
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.7\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2008-05-12 14:33+0200\n"
 "Last-Translator: Thomas Waldmann <tw-public@gmx.de>\n"
 "Language-Team: German <moin-user@lists.sourceforge.net>\n"
@@ -503,13 +503,16 @@
 msgid "filter"
 msgstr "Filter"
 
+msgid "about"
+msgstr "ungefähr"
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
 "%(be)s results out of about %(pages)d pages."
 msgstr ""
 "Ergebnisse %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s von %(aboutHits)s %(bs)s%"
-"(hits)d%(be)s Ergebnisse aus ungefähr %(pages)d Seiten."
+"(hits)d%(be)s Ergebnissen aus ungefähr %(pages)d Seiten."
 
 msgid "seconds"
 msgstr "Sekunden"
@@ -590,6 +593,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr "Fehlendes Passwort. Bitte geben Sie Benutzername und Passwort ein."
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr "LDAP-Server %(server)s kann nicht erreicht werden."
+
 msgid "You need to log in."
 msgstr "Sie müssen sich anmelden."
 
@@ -613,6 +620,9 @@
 msgid "Choose this name"
 msgstr "Diesen Namen auswählen"
 
+msgid "This is not a valid username, choose a different one."
+msgstr "Dies ist kein gültiger Benutzername, wählen Sie einen anderen."
+
 msgid ""
 "The username you have chosen is already\n"
 "taken. If it is your username, enter your password below to associate\n"
@@ -637,9 +647,6 @@
 msgid "OpenID failure."
 msgstr "OpenID fehlgeschlagen."
 
-msgid "This is not a valid username, choose a different one."
-msgstr "Dies ist kein gültiger Benutzername, wählen Sie einen anderen."
-
 msgid "Your account is now associated to your OpenID."
 msgstr "Ihr Konto ist nun mit Ihrer OpenID verbunden."
 
@@ -1211,6 +1218,15 @@
 msgid "Quick links"
 msgstr "Expressverweise"
 
+msgid "OpenID server"
+msgstr "OpenID-Server"
+
+msgid "The selected websites have been removed."
+msgstr "Die ausgewählten Web-Sites wurden entfernt."
+
+msgid "Trusted websites"
+msgstr "Web-Sites, denen wir vertrauen"
+
 #, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1355,14 +1371,6 @@
 "Verweis zu Dateianhang: %(attach)s\n"
 "Verweis zu Seite: %(page)s\n"
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr "XML-RPC-Fehler: %s"
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr "Kommunikationsfehler auf unterer Ebene: %s"
-
 msgid "Attachment link"
 msgstr "Verweis zu Dateianhang"
 
@@ -1739,30 +1747,18 @@
 msgid "index unavailable"
 msgstr "Index nicht verfügbar"
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "Xapian und/oder Python-Xapian-Bindings nicht installiert"
+
 msgid "N/A"
 msgstr "k.A."
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "Xapian und/oder Python-Xapian-Bindings nicht installiert"
-
 msgid "Xapian search"
 msgstr "Xapian-Suche"
 
-msgid "Xapian Version"
-msgstr "Xapian-Version"
-
-msgid "PyStemmer not installed"
-msgstr "PyStemmer ist nicht installiert"
-
 msgid "Stemming for Xapian"
 msgstr "Stamm-Bildung für Xapian"
 
-msgid "PyStemmer Version"
-msgstr "PyStemmer Version"
-
-msgid "PyStemmer stems"
-msgstr "PyStemmer Stämme"
-
 msgid "Active threads"
 msgstr "Aktive Threads"
 
@@ -1836,6 +1832,14 @@
 "Bitte verwenden Sie einen selektiveren Suchbegriff anstatt {{{\"%s\"}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+"Ihre Suchanfrage {{{\"%s\"}}} ist ungültig. Siehe HilfeZumSuchen für weitere "
+"Informationen."
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "Neuen Dateianhang \"%(filename)s\" hochladen"
 
@@ -2314,9 +2318,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2338,9 +2342,9 @@
 "\n"
 "||'''Neue persönliche Seite hinzufügen:'''||'''Zugeordnete ACL-Gruppe:'''||\n"
 "||<<NewPage(HomepageReadWritePageTemplate,Seite (read/write),%(username)s)"
-">>||[\"%(username)s/ReadWriteGroup\"]||\n"
+">>||[[%(username)s/ReadWriteGroup]]||\n"
 "||<<NewPage(HomepageReadPageTemplate,Seite (read-only),%(username)s)>>;;;||"
-"[\"%(username)s/ReadGroup\"]||\n"
+"[[%(username)s/ReadGroup]]||\n"
 "||<<NewPage(HomepagePrivatePageTemplate,Seite (privat),%(username)s)>>||nur %"
 "(username)s||\n"
 "\n"
@@ -2590,14 +2594,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-"Ihre Suchanfrage {{{\"%s\"}}} ist ungültig. Siehe HilfeZumSuchen für weitere "
-"Informationen."
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2616,7 +2612,7 @@
 "results of your search query in this wiki. <<BR>>"
 msgstr ""
 "(!) Sie führen eine Titelsuche durch, die möglicherweise nicht alle "
-"relevanten Ergebnisse Ihrer Sucheanfrage in diesem Wiki enthält. <<BR>>"
+"relevanten Ergebnisse Ihrer Suchanfrage in diesem Wiki enthält. <<BR>>"
 
 msgid "Click here to perform a full-text search with your search terms!"
 msgstr "Hier klicken für eine Volltextsuche mit diesen Suchbegriffen!"
@@ -2938,39 +2934,49 @@
 msgstr "Sie dürfen keine Anhänge dieser Seite auspacken."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
-msgstr ""
-"Dateianhang '%(filename)s' konnte nicht ausgepackt werden, weil die "
-"ausgepackten Dateien zu groß wären (%(space)d kB fehlen)."
+msgid "The file %(filename)s is not a .zip file."
+msgstr "Die Datei %(filename)s ist keine .zip-Datei."
 
 #, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
 msgstr ""
-"Dateianhang '%(filename)s' konnte nicht ausgepackt werden, weil die "
-"ausgepackten Dateien zu viele wären (%(count)d fehlen)."
+"Dateianhang '%(filename)s' wurde nicht ausgepackt, weil einige Dateien im "
+"Archiv entweder nicht im selben Verzeichnis sind oder die erlaubte "
+"Maximalgröße einer Einzeldatei (%(maxsize_file)d kB) überschritten haben."
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
+msgstr ""
+"Dateianhang '%(filename)s' wurde nicht ausgepackt, weil es den maximal "
+"erlaubten Speicherplatz für Dateianhänge pro Seite (%(size)d kB) "
+"überschritten hätte."
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
+msgstr ""
+"Dateianhang '%(filename)s' wurde nicht ausgepackt, weil es die maximal "
+"erlaubte Anzahl von Dateianhängen pro Seite (%(count)d) überschritten hätte."
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr ""
+"Dateianhang '%(filename)s' wurde teilweise ausgepackt (nicht überschrieben "
+"wurden: %(filelist)s)."
 
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "Dateianhang '%(filename)s' wurde ausgepackt."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-"Dateianhang '%(filename)s' wurde nicht ausgepackt, weil die Datei zu groß "
-"sind, weil nur .zip-Dateien erlaubt sind, weil sie bereits existieren oder "
-"weil Dateien in Ordnern enthalten sind."
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "Die Datei %(filename)s ist keine .zip-Datei."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "Dateianhang '%(filename)s'"
 
@@ -3043,3 +3049,21 @@
 
 msgid "Login and try again."
 msgstr "Melden Sie sich an und probieren Sie es noch einmal."
+
+#~ msgid "Xapian Version"
+#~ msgstr "Xapian-Version"
+
+#~ msgid "PyStemmer not installed"
+#~ msgstr "PyStemmer ist nicht installiert"
+
+#~ msgid "PyStemmer Version"
+#~ msgstr "PyStemmer Version"
+
+#~ msgid "PyStemmer stems"
+#~ msgstr "PyStemmer Stämme"
+
+#~ msgid "XML RPC error: %s"
+#~ msgstr "XML-RPC-Fehler: %s"
+
+#~ msgid "Low-level communication error: %s"
+#~ msgstr "Kommunikationsfehler auf unterer Ebene: %s"
--- a/MoinMoin/i18n/el.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/el.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language el
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2007-12-22 17:56+0200\n"
 "Last-Translator: YiannisValassakis <tungolcild@gmail.com>\n"
 "Language-Team: Greek <moin-devel@lists.sourceforge.net>\n"
@@ -474,6 +474,9 @@
 msgid "filter"
 msgstr ""
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -561,6 +564,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr ""
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 msgid "You need to log in."
 msgstr ""
 
@@ -579,6 +586,9 @@
 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"
@@ -599,9 +609,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1162,6 +1169,15 @@
 msgid "Quick links"
 msgstr "Γρήγορες συνδέσεις"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1292,14 +1308,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "Επισυναπτόμενα αρχεία"
@@ -1626,30 +1634,18 @@
 msgid "index unavailable"
 msgstr ""
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr ""
+
 msgid "N/A"
 msgstr "Δ/Α"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr ""
-
 msgid "Xapian search"
 msgstr ""
 
-msgid "Xapian Version"
-msgstr ""
-
-msgid "PyStemmer not installed"
-msgstr ""
-
 msgid "Stemming for Xapian"
 msgstr ""
 
-msgid "PyStemmer Version"
-msgstr ""
-
-msgid "PyStemmer stems"
-msgstr ""
-
 msgid "Active threads"
 msgstr ""
 
@@ -1725,6 +1721,12 @@
 "{{{\"%s\"}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr ""
 
@@ -2126,9 +2128,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2376,12 +2378,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2685,15 +2681,32 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+msgid "The file %(filename)s is not a .zip file."
 msgstr ""
 
 #, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
 msgstr ""
 
 #, python-format
@@ -2701,16 +2714,6 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr ""
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr ""
 
--- a/MoinMoin/i18n/en.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/en.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -11,7 +11,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.5\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-22 22:32+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2005-01-8 00:00+0100\n"
 "Last-Translator: Thomas Waldmann <tw-public@gmx.de>\n"
 "Language-Team: German <moin-devel@lists.sourceforge.net>\n"
@@ -441,6 +441,9 @@
 msgid "filter"
 msgstr ""
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -526,6 +529,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr ""
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 msgid "You need to log in."
 msgstr ""
 
@@ -544,6 +551,9 @@
 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"
@@ -564,9 +574,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1116,6 +1123,15 @@
 msgid "Quick links"
 msgstr ""
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1222,14 +1238,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 msgid "Attachment link"
 msgstr ""
 
@@ -1552,30 +1560,18 @@
 msgid "index unavailable"
 msgstr ""
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr ""
+
 msgid "N/A"
 msgstr ""
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr ""
-
 msgid "Xapian search"
 msgstr ""
 
-msgid "Xapian Version"
-msgstr ""
-
-msgid "PyStemmer not installed"
-msgstr ""
-
 msgid "Stemming for Xapian"
 msgstr ""
 
-msgid "PyStemmer Version"
-msgstr ""
-
-msgid "PyStemmer stems"
-msgstr ""
-
 msgid "Active threads"
 msgstr ""
 
@@ -2049,9 +2045,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2582,15 +2578,32 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+msgid "The file %(filename)s is not a .zip file."
 msgstr ""
 
 #, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
+msgstr ""
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
 msgstr ""
 
 #, python-format
@@ -2598,16 +2611,6 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr ""
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr ""
 
--- a/MoinMoin/i18n/es.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/es.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language es
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2007-11-24 23:12-0300\n"
 "Last-Translator: Ramiro Morales <rm0@gmx.net>\n"
 "Language-Team: Spanish <moin-devel@lists.sourceforge.net>\n"
@@ -504,6 +504,9 @@
 msgid "filter"
 msgstr "filtro"
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -595,6 +598,10 @@
 "Faltó ingresar la contraseña. Por favor, ingresa el nombre de usuario y "
 "contraseña"
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 #, fuzzy
 msgid "You need to log in."
 msgstr "Has salido del sistema."
@@ -614,6 +621,9 @@
 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"
@@ -635,9 +645,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1204,6 +1211,15 @@
 msgid "Quick links"
 msgstr "Enlaces rápidos"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1341,14 +1357,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "Adjuntos"
@@ -1736,30 +1744,18 @@
 msgid "index unavailable"
 msgstr "índice no disponible"
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "No está instalado Xapian o la interfaz Python Xapian"
+
 msgid "N/A"
 msgstr "Sin información"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "No está instalado Xapian o la interfaz Python Xapian"
-
 msgid "Xapian search"
 msgstr "Búsqueda xapian"
 
-msgid "Xapian Version"
-msgstr "Versión Xapian"
-
-msgid "PyStemmer not installed"
-msgstr "No está instalado PyStemmer"
-
 msgid "Stemming for Xapian"
 msgstr "Lematización para Xapian"
 
-msgid "PyStemmer Version"
-msgstr "Versión de PyStemmer"
-
-msgid "PyStemmer stems"
-msgstr "lexemas de PyStemmer"
-
 msgid "Active threads"
 msgstr "Hilos (conversaciones) activos"
 
@@ -1832,6 +1828,14 @@
 msgstr "Por favor, usa un término de búsqueda más específico que {{{\"%s\"}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+"La búsqueda de {{{\"%s\"}}} no es válida. Por favor visita "
+"AyudaSobreBúsquedas para más información."
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "Subir nuevo adjunto \"%(filename)s\""
 
@@ -2266,7 +2270,7 @@
 msgid "Please first create a homepage before creating additional pages."
 msgstr "Por favor crea una página personal antes de crear páginas adicionales."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
 "You can add some additional sub pages to your already existing homepage "
 "here.\n"
@@ -2286,9 +2290,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2569,14 +2573,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-"La búsqueda de {{{\"%s\"}}} no es válida. Por favor visita "
-"AyudaSobreBúsquedas para más información."
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2910,39 +2906,46 @@
 msgstr "No tienes permisos para descomprimir adjuntos de esta página."
 
 #, python-format
+msgid "The file %(filename)s is not a .zip file."
+msgstr "El archivo %(filename)s no es un archivo .zip."
+
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr ""
+"Los archivos adjuntos '%(filename)s' no se descomprimieron porque los "
+"archivos son demasiado grandes, solamente se admiten .zip, o ya existen."
+
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
 msgstr ""
 "El adjunto '%(filename)s' no pudo ser descomprimido porque los archivos "
 "resultantes serían demasiado largos (faltan %(space)d kB de espacio en "
 "disco)."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
 msgstr ""
 "El adjunto '%(filename)s' no se pudo descomprimir porque habría demasiados "
 "archivos resultantes (%(count)d archivos faltantes)."
 
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr "El adjunto '%(filename)s' fue descomprimido."
+
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "El adjunto '%(filename)s' fue descomprimido."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-"Los archivos adjuntos '%(filename)s' no se descomprimieron porque los "
-"archivos son demasiado grandes, solamente se admiten .zip, o ya existen."
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "El archivo %(filename)s no es un archivo .zip."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "Adjunto '%(filename)s'"
 
@@ -3015,6 +3018,18 @@
 msgid "Login and try again."
 msgstr "Ingresa al sistema e inténtalo nuevamente."
 
+#~ msgid "Xapian Version"
+#~ msgstr "Versión Xapian"
+
+#~ msgid "PyStemmer not installed"
+#~ msgstr "No está instalado PyStemmer"
+
+#~ msgid "PyStemmer Version"
+#~ msgstr "Versión de PyStemmer"
+
+#~ msgid "PyStemmer stems"
+#~ msgstr "lexemas de PyStemmer"
+
 #~ msgid "New Page or New Attachment"
 #~ msgstr "Nueva página o nuevo adjunto"
 
--- a/MoinMoin/i18n/fa.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/fa.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language fa
 
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: 1.6.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
 "PO-Revision-Date: 2005-11-06 17:11+0330\n"
 "Last-Translator: Mehdi Hassanpour <h.mehdi@gmail.com>\n"
 "Language-Team: Persian <h.mehdi@gmail.com>\n"
@@ -480,6 +480,9 @@
 msgid "filter"
 msgstr ""
 
+msgid "about"
+msgstr ""
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -577,6 +580,10 @@
 "کلمه رمز وارد نشده است. لطفا نام کاربری و کلمه عبور صحیح خود را مجددا وارد "
 "نمایید."
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr ""
+
 #, fuzzy
 msgid "You need to log in."
 msgstr "شما از سیستم خارج شدید."
@@ -596,6 +603,9 @@
 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"
@@ -617,9 +627,6 @@
 msgid "OpenID failure."
 msgstr ""
 
-msgid "This is not a valid username, choose a different one."
-msgstr ""
-
 msgid "Your account is now associated to your OpenID."
 msgstr ""
 
@@ -1194,6 +1201,15 @@
 msgid "Quick links"
 msgstr "لینکهای دسترسی سریع"
 
+msgid "OpenID server"
+msgstr ""
+
+msgid "The selected websites have been removed."
+msgstr ""
+
+msgid "Trusted websites"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1329,14 +1345,6 @@
 "Page link: %(page)s\n"
 msgstr ""
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
 #, fuzzy
 msgid "Attachment link"
 msgstr "ضمیمه ها"
@@ -1727,33 +1735,18 @@
 msgid "index unavailable"
 msgstr ""
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr ""
+
 msgid "N/A"
 msgstr "N/A"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr ""
-
 msgid "Xapian search"
 msgstr "Xapian search"
 
-#, fuzzy
-msgid "Xapian Version"
-msgstr "Xapian search"
-
-msgid "PyStemmer not installed"
-msgstr ""
-
 msgid "Stemming for Xapian"
 msgstr ""
 
-#, fuzzy
-msgid "PyStemmer Version"
-msgstr "نسخه python"
-
-#, fuzzy
-msgid "PyStemmer stems"
-msgstr "نسخه python"
-
 msgid "Active threads"
 msgstr "مباحث فعال"
 
@@ -1832,6 +1825,12 @@
 msgstr "لطفا به جای {{{\"%s\"}}} جستجوی گزیده تری انجام دهید"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "ارسال ضمیمه جدید \"%(filename)s\""
 
@@ -2234,7 +2233,7 @@
 msgid "Please first create a homepage before creating additional pages."
 msgstr "لطفا پیش از ساخت صفحات در این سایت٬ یک صفحه خانگی برای خود بسازید."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
 "You can add some additional sub pages to your already existing homepage "
 "here.\n"
@@ -2254,9 +2253,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2540,12 +2539,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2873,37 +2866,44 @@
 msgid "You are not allowed to unzip attachments of this page."
 msgstr "شما مجاز نیستید ضمیمه های فشرده این صفحه را باز کنید."
 
-#, python-format
+#, fuzzy, python-format
+msgid "The file %(filename)s is not a .zip file."
+msgstr "فایل %(target)s یک فایل zip نیست."
+
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
+msgstr "ضمیمه '%(filename)s' باز نشد."
+
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
 msgstr ""
 "ضمیمه '%(filename)s' به دلیل اینکه پس از باز شدن حجم زیادی دارد٬ باز نشد (%"
 "(space)d kB missing)."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
 msgstr ""
 "ضمیمه·'%(filename)s'·به·دلیل·اینکه·پس·از·باز·شدن·تعداد فایلها بسیار زیاد "
 "است٬·باز·نشد.(%(count)d·missing)."
 
+#, fuzzy, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr "ضمیمه '%(filename)s' از حالت فشرده خارج شد."
+
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "ضمیمه '%(filename)s' از حالت فشرده خارج شد."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr "ضمیمه '%(filename)s' باز نشد."
-
-#, fuzzy, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "فایل %(target)s یک فایل zip نیست."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "فایل ضمیمه '%(filename)s'"
 
@@ -2976,6 +2976,18 @@
 msgstr "لطفا وارد سایت شده و مجددا سعی نمایید."
 
 #, fuzzy
+#~ msgid "Xapian Version"
+#~ msgstr "Xapian search"
+
+#, fuzzy
+#~ msgid "PyStemmer Version"
+#~ msgstr "نسخه python"
+
+#, fuzzy
+#~ msgid "PyStemmer stems"
+#~ msgstr "نسخه python"
+
+#, fuzzy
 #~ msgid "New Page or New Attachment"
 #~ msgstr "ضمیمه جدید"
 
--- a/MoinMoin/i18n/fi.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/fi.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,18 +1,21 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language fi
 
+#
+# MoinMoin fi system text translation
+#
 msgid ""
 msgstr ""
-"Project-Id-Version: MoinMoin 1.6\n"
+"Project-Id-Version: MoinMoin 1.7\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
-"PO-Revision-Date: 2008-05-21 20:46+0300\n"
-"Last-Translator: Ville-Pekka Vainio <vpivaini@cs.helsinki.fi>\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
+"PO-Revision-Date: 2008-06-13 18:25+0300\n"
+"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>\n"
 "Language-Team: Finnish <moin-devel@lists.sourceforge.net>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
@@ -25,8 +28,8 @@
 msgid "<unknown>"
 msgstr "<tuntematon>"
 
+# Is ”asettamiseksi uudelleen” ok in Finnish?
 #, python-format
-# Is ”asettamiseksi uudelleen” ok in Finnish?
 msgid ""
 "Login Name: %s\n"
 "\n"
@@ -38,7 +41,7 @@
 "\n"
 "Salasanan palautustunniste: %s\n"
 "\n"
-"Osoite salasanan asettamiseksi uudelleen: %s/?action=recoverpass&name=%s&token=%s\n"
+"Vaihda salasana osoitteessa %s/?action=recoverpass&name=%s&token=%s\n"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -348,7 +351,7 @@
 msgstr "Et ole sivun ylläpitäjä, joten et voi muuttaa pääsynvalvontalistoja!"
 
 msgid "Notifications sent to:"
-msgstr "Ilmoituksia lähetetty:"
+msgstr "Ilmoitus lähetetty seuraaville käyttäjille:"
 
 #, python-format
 msgid ""
@@ -492,6 +495,9 @@
 msgid "filter"
 msgstr "suodatin"
 
+msgid "about"
+msgstr "noin"
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -579,6 +585,10 @@
 msgid "Missing password. Please enter user name and password."
 msgstr "Salasana puuttuu. Anna käyttäjänimi ja salasana."
 
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr "Tiedonvaihto LDAP-palvelimen %(server)s kanssa ei onnistunut."
+
 msgid "You need to log in."
 msgstr "Sinun on kirjauduttava sisään."
 
@@ -588,8 +598,8 @@
 "one now</a>. <a href=\"%(sendmypasswordlink)s\">Forgot your password?</a>"
 msgstr ""
 "Jos sinulla ei ole käyttäjätunnusta, <a href=\"%(userprefslink)s\">voit "
-"luoda sen nyt</a>. <a href=\"%(sendmypasswordlink)s\">Unohditko salasanasi?"
-"</a>"
+"luoda sen nyt</a>. <a href=\"%(sendmypasswordlink)s\">Unohditko salasanasi?</"
+"a>"
 
 msgid ""
 "Please choose an account name now.\n"
@@ -603,6 +613,9 @@
 msgid "Choose this name"
 msgstr "Valitse tämä nimi"
 
+msgid "This is not a valid username, choose a different one."
+msgstr "Tämä käyttäjätunnus on virheellinen, valitse toinen."
+
 msgid ""
 "The username you have chosen is already\n"
 "taken. If it is your username, enter your password below to associate\n"
@@ -627,9 +640,6 @@
 msgid "OpenID failure."
 msgstr "OpenID-toimintahäiriö."
 
-msgid "This is not a valid username, choose a different one."
-msgstr "Tämä käyttäjätunnus on virheellinen, valitse toinen."
-
 msgid "Your account is now associated to your OpenID."
 msgstr "Käyttäjätunnuksesi on nyt liitetty OpenID:hesi."
 
@@ -655,8 +665,8 @@
 "If you do not have an account yet, you can still log in with your OpenID and "
 "create one during login."
 msgstr ""
-"Jos sinulla ei vielä ole käyttäjätunnusta, voit silti kirjautua OpenID:lläsi ja "
-"luoda tunnuksen kirjautumisen aikana."
+"Jos sinulla ei vielä ole käyttäjätunnusta, voit silti kirjautua OpenID:lläsi "
+"ja luoda tunnuksen kirjautumisen aikana."
 
 msgid "Failed to connect to database."
 msgstr "Tietokantaan yhdistäminen epäonnistui."
@@ -1072,8 +1082,8 @@
 "You can now change the settings of the selected user account; log out to get "
 "back to your account."
 msgstr ""
-"Voit nyt vaihtaa valitun käyttäjätunnuksen asetuksia. Kirjaudu ulos päästäksesi "
-"takaisin omaan tunnukseesi."
+"Voit nyt vaihtaa valitun käyttäjätunnuksen asetuksia. Kirjaudu ulos "
+"päästäksesi takaisin omaan tunnukseesi."
 
 msgid "You are the only user."
 msgstr "Olet ainoa käyttäjä."
@@ -1197,6 +1207,17 @@
 msgid "Quick links"
 msgstr "Pikalinkit"
 
+#, fuzzy
+msgid "OpenID server"
+msgstr "OpenID:tä ei palveltu"
+
+#, fuzzy
+msgid "The selected websites have been removed."
+msgstr "Valitut OpenID:t on poistettu."
+
+msgid "Trusted websites"
+msgstr ""
+
 #, python-format
 msgid ""
 "Dear Wiki user,\n"
@@ -1229,8 +1250,7 @@
 "\n"
 msgstr ""
 "Hei!\n"
-"Olet tilannut wikin \"%(sitename)s\" sivun "
-"muutosilmoitukset.\n"
+"Olet tilannut wikin \"%(sitename)s\" sivun muutosilmoitukset.\n"
 "\n"
 "%(editor)s on poistanut sivun \"%(pagename)s\":\n"
 "\n"
@@ -1246,11 +1266,10 @@
 "s:\n"
 msgstr ""
 "Hei!\n"
-"Olet tilannut wikin \"%(sitename)s\" sivun "
-"muutosilmoitukset.\n"
+"Olet tilannut wikin \"%(sitename)s\" sivun muutosilmoitukset.\n"
 "\n"
-"%(editor)s on nimennyt sivun \"%(oldname)s\" uudelleen, sen uusi nimi on "
-" \"%(pagename)s\":\n"
+"%(editor)s on nimennyt sivun \"%(oldname)s\" uudelleen, sen uusi nimi on  \"%"
+"(pagename)s\":\n"
 
 #, python-format
 msgid "New user account created on %(sitename)s"
@@ -1285,8 +1304,8 @@
 "Attachment size: %(attach_size)s\n"
 msgstr ""
 "Hei!\n"
-"Olet tilannut wikisivun \"%(page_name)s\" muutosilmoitukset. Käyttäjä "
-"%(editor)s on lisännyt tälle sivulle liitteen. Lisätietoja:\n"
+"Olet tilannut wikisivun \"%(page_name)s\" muutosilmoitukset. Käyttäjä %"
+"(editor)s on lisännyt tälle sivulle liitteen. Lisätietoja:\n"
 "\n"
 "Liitteen nimi: %(attach_name)s\n"
 "Liitteen koko: %(attach_size)s\n"
@@ -1335,14 +1354,6 @@
 "Linkki liitteeseen: %(attach)s\n"
 "Linkki sivulle: %(page)s\n"
 
-#, python-format
-msgid "XML RPC error: %s"
-msgstr "XML RPC -virhe: %s"
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr "Matalan tason yhteysvirhe: %s"
-
 msgid "Attachment link"
 msgstr "Linkki liitteeseen"
 
@@ -1443,8 +1454,8 @@
 "kursiivi'''''<<Verbatim(''''')>>; <<Verbatim('')>>''sekä ''<<Verbatim(''')"
 ">>'''''lihavoitua'''<<Verbatim(''')>> että kursiivia''<<Verbatim('')>>; "
 "<<Verbatim(----)>> vaakatason viiva.\n"
-" Otsikot:: = Otsikko 1 =; == Otsikko 2 ==; === Otsikko 3 ===; "
-"==== Otsikko 4 ====; ===== Otsikko 5 =====.\n"
+" Otsikot:: = Otsikko 1 =; == Otsikko 2 ==; === Otsikko 3 ===; ==== Otsikko 4 "
+"====; ===== Otsikko 5 =====.\n"
 " Luettelot:: väli ja jokin näistä: * numeroimaton kohta; 1., a., A., i., I. "
 "numeroitu kohta; 1.#n aloita numerointi n:stä; pelkkä väli sisentää.\n"
 " Linkit:: <<Verbatim(YhteenIsollaKirjoitetutSanat)>>; <<Verbatim([[kohde|"
@@ -1514,10 +1525,10 @@
 ">>'''''lihavoitua'''<<Verbatim(***)>> että kursiivia''<<Verbatim(//)>>;\n"
 " Vaakatason viiva:: <<Verbatim(----)>>\n"
 " Pakotettu rivinvaihto:: <<Verbatim(\\\\)>>\n"
-" Otsikot:: = Otsikko 1 =; == Otsikko 2 ==; === Otsikko 3 ===; "
-"==== Otsikko 4 ====; ===== Otsikko 5 =====.\n"
-" Luettelot:: * numeroimaton kohta; ** numeroimaton alikohta; "
-"# numeroitu kohta; ## numeroitu alikohta.\n"
+" Otsikot:: = Otsikko 1 =; == Otsikko 2 ==; === Otsikko 3 ===; ==== Otsikko 4 "
+"====; ===== Otsikko 5 =====.\n"
+" Luettelot:: * numeroimaton kohta; ** numeroimaton alikohta; # numeroitu "
+"kohta; ## numeroitu alikohta.\n"
 " Linkit:: <<Verbatim([[kohde]])>>; <<Verbatim([[kohde|linkkiteksti]])>>.\n"
 " Taulukot:: |= otsikkoteksti | tekstiä solussa | lisää tekstiä solussa |;\n"
 "\n"
@@ -1718,30 +1729,18 @@
 msgid "index unavailable"
 msgstr "hakemisto ei käytettävissä"
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "Xapian- ja/tai Python Xapian -sidonnaisia ei ole asennettu"
+
 msgid "N/A"
 msgstr "ei ole"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "Xapian- ja/tai Python Xapian -sidonnaisia ei ole asennettu"
-
 msgid "Xapian search"
 msgstr "Xapian-haku"
 
-msgid "Xapian Version"
-msgstr "Xapian, versio"
-
-msgid "PyStemmer not installed"
-msgstr "PyStemmeriä ei ole asennettu"
-
 msgid "Stemming for Xapian"
 msgstr "Xapian-sanataivutus"
 
-msgid "PyStemmer Version"
-msgstr "PyStemmer, versio"
-
-msgid "PyStemmer stems"
-msgstr "PyStemmerin taivutuskielet"
-
 msgid "Active threads"
 msgstr "Aktiiviset säikeet"
 
@@ -1814,6 +1813,14 @@
 msgstr "Käytä tarkempaa hakuehtoa kuin {{{\"%s\"}}}"
 
 #, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+"Hakuehto {{{\"%s\"}}} ei ole kelvollinen. Lisätietoja on sivulla "
+"OhjeHakemisesta."
+
+#, python-format
 msgid "Upload new attachment \"%(filename)s\""
 msgstr "Lähetä uusi liitetiedosto \"%(filename)s\""
 
@@ -1822,8 +1829,8 @@
 "%(extension_name)s %(extension_type)s: Required argument %(argument_name)s "
 "missing."
 msgstr ""
-"%(extension_name)s %(extension_type)s: Pakollinen argumentti %(argument_name)s "
-"puuttuu."
+"%(extension_name)s %(extension_type)s: Pakollinen argumentti %(argument_name)"
+"s puuttuu."
 
 #, python-format
 msgid ""
@@ -2091,12 +2098,13 @@
 "delegation on its own.)"
 msgstr ""
 "\n"
-"Jos hyväksyt, trust rootin edustamalle sivustolle kerrotaan, että hallitset\n"
+"Jos hyväksyt, OpenID-luottamusjuuren (trust rootin) edustamalle sivustolle "
+"kerrotaan, että hallitset\n"
 "identiteetti-URL:ää %s. (Jos käytät delegoitua identiteettiä, sivusto\n"
 "huolehtii delegaation kääntämisestä itse.)"
 
 msgid "Trust root"
-msgstr ""
+msgstr "OpenID-luottamusjuuri (trust root)"
 
 msgid "Identity URL"
 msgstr "Identiteetti-URL"
@@ -2265,7 +2273,7 @@
 msgid "Please first create a homepage before creating additional pages."
 msgstr "Luo kotisivu, ennen kuin luot muita sivuja."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
 "You can add some additional sub pages to your already existing homepage "
 "here.\n"
@@ -2285,9 +2293,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2399,8 +2407,8 @@
 "empty, we derive the page name from the file name."
 msgstr ""
 "Voit ladata palvelimelle sisältöä alla nimettyä sivua varten. Jos muutat "
-"sivun nimeä, voir myös ladata palvelimelle sisältöä toista sivua varten. "
-"Jos sivun nimi on tyhjä, sivu nimetään tiedoston nimen mukaan."
+"sivun nimeä, voit myös ladata palvelimelle sisältöä toista sivua varten. Jos "
+"sivun nimi on tyhjä, sivu nimetään tiedoston nimen mukaan."
 
 msgid "File to load page content from"
 msgstr "Tiedosto, josta ladataan sisältöä"
@@ -2424,8 +2432,8 @@
 "Add your email address or Jabber ID in your user settings to use "
 "subscriptions."
 msgstr ""
-"Lisää sähköpostiosoitteesi tai Jabber-tunnuksesi asetuksiisi "
-"käyttääksesi tilauksia."
+"Lisää sähköpostiosoitteesi tai Jabber-tunnuksesi asetuksiisi käyttääksesi "
+"tilauksia."
 
 msgid "You are already subscribed to this page."
 msgstr "Olet jo tilannut tämän sivun."
@@ -2554,14 +2562,6 @@
 
 #, python-format
 msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-"Hakuehto {{{\"%s\"}}} ei ole kelvollinen. Lisätietoja on sivulla "
-"OhjeHakemisesta."
-
-#, python-format
-msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
@@ -2838,8 +2838,8 @@
 "Attachment '%(pagename)s/%(filename)s' moved to '%(new_pagename)s/%"
 "(new_filename)s'."
 msgstr ""
-"Liitetiedosto '%(pagename)s/%(filename)s' on siirretty sivulle "
-"'%(new_pagename)s/%(new_filename)s'."
+"Liitetiedosto '%(pagename)s/%(filename)s' on siirretty sivulle '%"
+"(new_pagename)s/%(new_filename)s'."
 
 msgid "Nothing changed"
 msgstr "Muutoksia ei tehty"
@@ -2892,39 +2892,48 @@
 msgstr "Sinulla ei ole oikeutta purkaa tämän sivun liitetiedostoja."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
-msgstr ""
-"Liitetiedostoa '%(filename)s' ei voitu purkaa, sillä tiedoston sisältö "
-"purettuna veisi liikaa tilaa (%(space)d kilotavua liikaa)."
+msgid "The file %(filename)s is not a .zip file."
+msgstr "Tiedosto %(filename)s ei ole zip-tiedosto."
 
 #, python-format
 msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
+"Attachment '%(filename)s' not unzipped because some files in the zip are "
+"either not in the same directory or exceeded the single file size limit (%"
+"(maxsize_file)d kB)."
 msgstr ""
-"Liitetiedostoa '%(filename)s' ei voitu purkaa, sillä purettuna tiedostoja "
-"olisi liian monta (%(count)d liikaa)."
+"Liitetiedostoa '%(filename)s' ei purettu, koska sen sisältämät tiedostot "
+"eivät sijaitse samassa hakemistossa tai tiedostot ylittävät kokorajoituksen, "
+"joka on (%(maxsize_file)d kilotavua)."
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment storage size limit (%(size)d kB)."
+msgstr ""
+"Liitetiedostoa '%(filename)s' ei purettu, sillä purettava tiedosto ylittää "
+"kooltaan sivukohtaisen kokorajoituksen, joka on (%(size)d kilotavua)."
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' not unzipped because it would have exceeded the "
+"per page attachment count limit (%(count)d)."
+msgstr ""
+"Liitetiedostoa '%(filename)s' ei purettu, sillä purettuna tiedostojen määrä "
+"ylittäisi sivukohtaisen rajoituksen, joka on (%(count)d) tiedostoa."
+
+#, python-format
+msgid ""
+"Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)"
+"s)."
+msgstr ""
+"Liitetiedosto '%(filename)s' on purettu osittain (seuraavia olemassa olevia "
+"tiedostoja ei korvattu: %(filelist)s)."
 
 #, python-format
 msgid "Attachment '%(filename)s' unzipped."
 msgstr "Liitetiedosto '%(filename)s' on purettu."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' not unzipped because the files are too big, .zip "
-"files only, exist already or reside in folders."
-msgstr ""
-"Liitettä '%(filename)s' ei purettu, koska sen sisältämät tiedostot ovat joko "
-"liian suuria, zip-tiedostoja, sijaitsevat alihakemistoissa tai ovat jo "
-"olemassa."
-
-#, python-format
-msgid "The file %(filename)s is not a .zip file."
-msgstr "Tiedosto %(filename)s ei ole zip-tiedosto."
-
-#, python-format
 msgid "Attachment '%(filename)s'"
 msgstr "Liitetiedosto '%(filename)s'"
 
@@ -2998,6 +3007,24 @@
 msgid "Login and try again."
 msgstr "Kirjaudu sisään ja yritä uudelleen."
 
+#~ msgid "Xapian Version"
+#~ msgstr "Xapian, versio"
+
+#~ msgid "PyStemmer not installed"
+#~ msgstr "PyStemmeriä ei ole asennettu"
+
+#~ msgid "PyStemmer Version"
+#~ msgstr "PyStemmer, versio"
+
+#~ msgid "PyStemmer stems"
+#~ msgstr "PyStemmerin taivutuskielet"
+
+#~ msgid "XML RPC error: %s"
+#~ msgstr "XML RPC -virhe: %s"
+
+#~ msgid "Low-level communication error: %s"
+#~ msgstr "Matalan tason yhteysvirhe: %s"
+
 #~ msgid "New Page or New Attachment"
 #~ msgstr "Uusi sivu tai liitetiedosto"
 
--- a/MoinMoin/i18n/fr.MoinMoin.po	Mon Jul 07 10:18:20 2008 +0900
+++ b/MoinMoin/i18n/fr.MoinMoin.po	Thu Jul 17 16:29:23 2008 +0900
@@ -1,8 +1,8 @@
-## Please edit system and help pages ONLY in the moinmaster wiki! For more
-## information, please see MoinMaster:MoinPagesEditorGroup.
+## Please edit system and help pages ONLY in the master wiki!
+## For more information, please see MoinMoin:MoinDev/Translation.
 ##master-page:None
 ##master-date:None
-#acl MoinPagesEditorGroup:read,write,delete,revert All:read
+#acl -All:write Default
 #format gettext
 #language fr
 
@@ -13,8 +13,8 @@
 msgstr ""
 "Project-Id-Version: moin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-12 23:52+0200\n"
-"PO-Revision-Date: 2008-03-25 05:16+0100\n"
+"POT-Creation-Date: 2008-06-21 22:47+0200\n"
+"PO-Revision-Date: 2008-06-12 00:32+0200\n"
 "Last-Translator: Jean-Philippe Guérard <fevrier@tigreraye.org>\n"
 "Language-Team: Français\n"
 "MIME-Version: 1.0\n"
@@ -29,7 +29,7 @@
 msgid "<unknown>"
 msgstr "<inconnu>"
 
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "Login Name: %s\n"
 "\n"
@@ -37,11 +37,12 @@
 "\n"
 "Password reset URL: %s/?action=recoverpass&name=%s&token=%s\n"
 msgstr ""
-"Nom : %s\n"
+"Nom d'utilisateur : %s\n"
 "\n"
-"Mot de passe : %s\n"
+"Code de récupération du mot de passe : %s\n"
 "\n"
-"URL de connexion : %s/%s?action=login\n"
+"URL de remise à zéro du mot de passe : %s/?action=recoverpass&name=%s&token=%"
+"s\n"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -50,6 +51,13 @@
 "go to the password recovery page again and enter your username and the\n"
 "recovery token.\n"
 msgstr ""
+"Quelqu'un a demandé qu'un code de récupération du mot de passe\n"
+"vous soit envoyé.\n"
+"\n"
+"Si vous avez perdu votre mot de passe, allez à l'adresse de remise à\n"
+"zéro du mot de passe ci-dessous ou retournez à la page de\n"
+"récupération du mot de passe et entrez votre nom d'utilisateur et le\n"
+"code de récupération.\n"
 
 #, python-format
 msgid "[%(sitename)s] Your wiki account data"
@@ -59,31 +67,31 @@
 msgstr "Vous n'êtes pas autorisé à modifier cette page."
 
 msgid "Page is immutable!"
-msgstr "Page immuable !"
+msgstr "Page immuable !"
 
 msgid "Cannot edit old revisions!"
-msgstr "Impossible de modifier d'anciennes versions !"
+msgstr "Impossible de modifier d'anciennes versions !"
 
 msgid "The lock you held timed out. Be prepared for editing conflicts!"
 msgstr ""
 "Le verrou que vous déteniez à expiré. Vos modifications risquent d'engendrer "
-"des conflits !"
+"des conflits !"
 
 #, python-format
 msgid "Draft of \"%(pagename)s\""
-msgstr "Brouillon de « %(pagename)s »"
+msgstr "Brouillon de « %(pagename)s »"
 
 #, python-format
 msgid "Edit \"%(pagename)s\""
-msgstr "Éditer « %(pagename)s »"
+msgstr "Éditer « %(pagename)s »"
 
 #, python-format
 msgid "Preview of \"%(pagename)s\""
-msgstr "Aperçu de « %(pagename)s »"
+msgstr "Aperçu de « %(pagename)s »"
 
 #, python-format
 msgid "Your edit lock on %(lock_page)s has expired!"
-msgstr "Votre verrou sur %(lock_page)s a expiré !"
+msgstr "Votre verrou sur %(lock_page)s a expiré !"
 
 #, python-format
 msgid "Your edit lock on %(lock_page)s will expire in # minutes."
@@ -94,18 +102,18 @@
 msgstr "Votre verrou sur %(lock_page)s va expirer dans # secondes."
 
 msgid "Someone else deleted this page while you were editing!"
-msgstr "Quelqu'un a effacé la page alors que vous l'éditiez !"
+msgstr "Quelqu'un a effacé la page alors que vous l'éditiez !"
 
 msgid "Someone else changed this page while you were editing!"
-msgstr "Quelqu'un a modifié la page alors que vous l'éditiez !"
+msgstr "Quelqu'un a modifié la page alors que vous l'éditiez !"
 
 msgid ""
 "Someone else saved this page while you were editing!\n"
 "Please review the page and save then. Do not save this page as it is!"
 msgstr ""
-"Quelqu'un a enregistré cette page pendant que vous l'éditiez !\n"
+"Quelqu'un a enregistré cette page pendant que vous l'éditiez !\n"
 "Vérifiez d'abord la page puis enregistrez-la. N'enregistrez pas cette page "
-"telle quelle !"
+"telle quelle !"
 
 msgid "[Content loaded from draft]"
 msgstr "[Contenu chargé depuis le brouillon]"
@@ -131,7 +139,7 @@
 "preview, cancel an edit or unsuccessfully save."
 msgstr ""
 "'''<<BR>>Si vous avez perdu vos dernières modifications, vous pouvez "
-"utiliser le bouton « Recharger le brouillon » pour reprendre, à la place de "
+"utiliser le bouton « Recharger le brouillon » pour reprendre, à la place de "
 "la version actuelle (%(page_rev)d), le brouillon créé à partir de la version "
 "%(draft_rev)d (enregistré %(draft_timestamp_str)s).''' Un brouillon est "
 "conservé, à votre intention, chaque fois que vous utilisez l'aperçu, que "
@@ -139,7 +147,7 @@
 
 #, python-format
 msgid "Describe %s here."
-msgstr "Rédiger « %s » ici."
+msgstr "Rédiger « %s » ici."
 
 msgid "Check Spelling"
 msgstr "Vérifier l'orthographe"
@@ -175,57 +183,57 @@
 msgstr "Modification mineure"
 
 msgid "Comment:"
-msgstr "Commentaire :"
+msgstr "Commentaire :"
 
 msgid "<No addition>"
 msgstr "<Pas d'ajout>"
 
 #, python-format
 msgid "Add to: %(category)s"
-msgstr "Ajouter à : %(category)s"
+msgstr "Ajouter à : %(category)s"
 
 msgid "Remove trailing whitespace from each line"
 msgstr "Supprimer les espaces en fin de ligne"
 
 #, python-format
 msgid "Argument \"%s\" must be a boolean value, not \"%s\""
-msgstr "L'argument « %s » doit être un booléen, et non « %s »"
+msgstr "L'argument « %s » doit être un booléen, et non « %s »"
 
 #, python-format
 msgid "Argument must be a boolean value, not \"%s\""
-msgstr "L'argument doit être un booléen, et non « %s »"
+msgstr "L'argument doit être un booléen, et non « %s »"
 
 #, python-format
 msgid "Argument \"%s\" must be an integer value, not \"%s\""
-msgstr "L'argument « %s » doit être un entier, et non « %s »"
+msgstr "L'argument « %s » doit être un entier, et non « %s »"
 
 #, python-format
 msgid "Argument must be an integer value, not \"%s\""
-msgstr "L'argument doit être un entier, et non « %s »"
+msgstr "L'argument doit être un entier, et non « %s »"
 
 #, python-format
 msgid "Argument \"%s\" must be a floating point value, not \"%s\""
-msgstr "L'argument « %s » doit être un nombre à virgule flotante, et non « %s »"
+msgstr "L'argument « %s » doit être un nombre à virgule flotante, et non « %s »"
 
 #, python-format
 msgid "Argument must be a floating point value, not \"%s\""
-msgstr "L'argument doit être un nombre à virgule flotante, et non « %s »"
+msgstr "L'argument doit être un nombre à virgule flotante, et non « %s »"
 
 #, python-format
 msgid "Argument \"%s\" must be a complex value, not \"%s\""
-msgstr "L'argument « %s » doit être un nombre complexe, et non « %s »"
+msgstr "L'argument « %s » doit être un nombre complexe, et non « %s »"
 
 #, python-format
 msgid "Argument must be a complex value, not \"%s\""
-msgstr "L'argument doit être un nombre complexe, et non « %s »"
+msgstr "L'argument doit être un nombre complexe, et non « %s »"
 
 #, python-format
 msgid "Argument \"%s\" must be one of \"%s\", not \"%s\""
-msgstr "L'argument « %s » doit être l'une des valeurs « %s », et non « %s »"
+msgstr "L'argument « %s » doit être l'une des valeurs « %s », et non « %s »"
 
 #, python-format
 msgid "Argument must be one of \"%s\", not \"%s\""
-msgstr "L'argument doit être l'une des valeurs « %s », et non « %s »"
+msgstr "L'argument doit être l'une des valeurs « %s », et non « %s »"
 
 msgid "Too many arguments"
 msgstr "Trop d'arguments"
@@ -237,22 +245,22 @@
 
 #, python-format
 msgid "Argument \"%s\" is required"
-msgstr "L'argument « %s » est obligatoire"
+msgstr "L'argument « %s » est obligatoire"
 
 #, python-format
 msgid "No argument named \"%s\""
-msgstr "Aucun argument n'est nommé « %s »"
+msgstr "Aucun argument n'est nommé « %s »"
 
 #, python-format
 msgid "Expected \"=\" to follow \"%(token)s\""
-msgstr "« = » était attendu après « %(token)s »"
+msgstr "« = » était attendu après « %(token)s »"
 
 #, python-format
 msgid "Expected a value for key \"%(token)s\""
-msgstr "Une valeur était attendue pour la clef « %(token)s »"
+msgstr "Une valeur était attendue pour la clef « %(token)s »"
 
 msgid "Your changes are not saved!"
-msgstr "Vos modifications ne sont pas enregistrées !"
+msgstr "Vos modifications ne sont pas enregistrées !"
 
 msgid "Page name is too long, try shorter name."
 msgstr "Nom de page trop long, essayez un nom plus court."
@@ -267,7 +275,7 @@
 msgstr "Impossible de copier vers un nom de page vide."
 
 msgid "You are not allowed to copy this page!"
-msgstr "Vous n'êtes pas autorisé à copier cette page !"
+msgstr "Vous n'êtes pas autorisé à copier cette page !"
 
 #, python-format
 msgid ""
@@ -275,18 +283,18 @@
 "\n"
 "Try a different name."
 msgstr ""
-"'''Une page ayant pour nom « {{{%s}}} » existe déjà.'''\n"
+"'''Une page ayant pour nom « {{{%s}}} » existe déjà.'''\n"
 "\n"
 "Essayez un autre nom."
 
 #, python-format
 msgid "Could not copy page because of file system error: %s."
 msgstr ""
-"Impossible de renommer la page du fait d'une erreur du système de fichiers : "
+"Impossible de renommer la page du fait d'une erreur du système de fichiers : "
 "%s."
 
 msgid "You are not allowed to rename this page!"
-msgstr "Vous n'êtes pas autorisé à renommer cette page !"
+msgstr "Vous n'êtes pas autorisé à renommer cette page !"
 
 msgid "You can't rename to an empty pagename."
 msgstr "Impossible de renommer sans indiquer de nouveau nom."
@@ -294,11 +302,11 @@
 #, python-format
 msgid "Could not rename page because of file system error: %s."
 msgstr ""
-"Impossible de renommer la page à cause d'une erreur du système de fichier : %"
+"Impossible de renommer la page à cause d'une erreur du système de fichier : %"
 "s."
 
 msgid "You are not allowed to delete this page!"
-msgstr "Vous n'êtes pas autorisé à supprimer cette page !"
+msgstr "Vous n'êtes pas autorisé à supprimer cette page !"
 
 msgid "Thank you for your changes. Your attention to detail is appreciated."
 msgstr ""
@@ -307,21 +315,21 @@
 
 #, python-format
 msgid "Page \"%s\" was successfully deleted!"
-msgstr "Page « %s » supprimée avec succès !"
+msgstr "Page « %s » supprimée avec succès !"
 
 #, python-format
 msgid "Page could not get locked. Unexpected error (errno=%d)."
 msgstr "Impossible de verrouiller la page. Erreur inattendue (errno=%d)."
 
 msgid "Page could not get locked. Missing 'current' file?"
-msgstr "Impossible de verrouiller la page. Fichier « current » manquant ?"
+msgstr "Impossible de verrouiller la page. Fichier « current » manquant ?"
 
 #, python-format
 msgid ""
 "Unable to determine current page revision from the 'current' file. The page %"
 "s is damaged and cannot be edited right now."
 msgstr ""
-"Le fichier « current » ne permet pas de déterminer la version de la page en "
+"Le fichier « current » ne permet pas de déterminer la version de la page en "
 "cours. La page %s est endommagée et ne peut être modifiée pour l'instant."
 
 #, python-format
@@ -336,32 +344,32 @@
 "page %s (errno=%d)"
 
 msgid "You are not allowed to edit this page!"
-msgstr "Vous n'êtes pas autorisé à modifier cette page !"
+msgstr "Vous n'êtes pas autorisé à modifier cette page !"
 
 msgid "You cannot save empty pages."
 msgstr "Vous ne pouvez pas enregistrer de pages vides."
 
 msgid "You already saved this page!"
-msgstr "Vous avez déjà enregistré cette page !"
+msgstr "Vous avez déjà enregistré cette page !"
 
 msgid "You already edited this page! Please do not use the back button."
 msgstr ""
-"Vous avez déjà modifié cette page ! Veuillez ne pas utiliser le bouton "
-"« Précédent »."
+"Vous avez déjà modifié cette page ! Veuillez ne pas utiliser le bouton « "
+"Précédent »."
 
 msgid "You did not change the page content, not saved!"
 msgstr ""
 "Vous n'avez pas modifié le contenu de cette page, elle n'a pas été "
-"enregistrée !"
+"enregistrée !"
 
 msgid ""
 "You can't change ACLs on this page since you have no admin rights on it!"
 msgstr ""
 "Vous ne pouvez pas modifier la liste de contrôle d'accès (ACL) de cette "
-"page, car vous n'en avez pas les droits d'administration !"
+"page, car vous n'en avez pas les droits d'administration !"
 
 msgid "Notifications sent to:"
-msgstr ""
+msgstr "Notifications envoyées à :"
 
 #, python-format
 msgid ""
@@ -388,7 +396,7 @@
 
 msgid "Use the Preview button to extend the locking period."
 msgstr ""
-"Utilisez le bouton « Aperçu » pour prolonger la période de verrouillage."
+"Utilisez le bouton « Aperçu » pour prolonger la période de verrouillage."
 
 #, python-format
 msgid ""
@@ -411,7 +419,7 @@
 "dernière fois le %(timestamp)s.<<BR>> '''Afin d'éviter des conflits "
 "d'édition,  vous ''ne'' devriez ''pas'' éditer cette page avant au moins %"
 "(mins_valid)d nouvelles minutes.'''<<BR>>\n"
-"Pour quitter l'éditeur, utilisez le bouton « Annuler »."
+"Pour quitter l'éditeur, utilisez le bouton « Annuler »."
 
 msgid "The wiki is currently not reachable."
 msgstr "Il n'est pas possible pour l'instant de se connecter à ce wiki."
@@ -442,7 +450,7 @@
 
 #, python-format
 msgid "Installation of '%(filename)s' failed."
-msgstr "Enregistrement du dessin « %(filename)s »."
+msgstr "Enregistrement du dessin « %(filename)s »."
 
 #, python-format
 msgid "The file %s is not a MoinMoin package file."
@@ -453,7 +461,7 @@
 msgstr "La page %s n'existe pas."
 
 msgid "Invalid package file header."
-msgstr "Fichier d'en-tête du paquet non valide."
+msgstr "Le fichier d'en-tête du paquet est incorrect."
 
 msgid "Package file format unsupported."
 msgstr "Format de paquet non compatible."
@@ -469,7 +477,7 @@
 #, python-format
 msgid "Invalid highlighting regular expression \"%(regex)s\": %(error)s"
 msgstr ""
-"L'expression rationnelle de surlignage : « %(regex)s » est incorrecte : %"
+"L'expression rationnelle de surlignage : « %(regex)s » est incorrecte : %"
 "(error)s"
 
 msgid ""
@@ -477,7 +485,7 @@
 "search results!"
 msgstr ""
 "Le contenu sauvegardé de cette page est obsolète et ne sera pas inclus dans "
-"les résultats d'une recherche !"
+"les résultats d'une recherche !"
 
 #, python-format
 msgid "Revision %(rev)d as of %(date)s"
@@ -485,11 +493,11 @@
 
 #, python-format
 msgid "Redirected from page \"%(page)s\""
-msgstr "Redirigé depuis la page « %(page)s »"
+msgstr "Redirigé depuis la page « %(page)s »"
 
 #, python-format
 msgid "This page redirects to page \"%(page)s\""
-msgstr "Cette page vous redirige vers la page « %(page)s »"
+msgstr "Cette page vous redirige vers la page « %(page)s »"
 
 msgid "Create New Page"
 msgstr "Créer une page"
@@ -509,6 +517,9 @@
 msgid "filter"
 msgstr "filtre"
 
+msgid "about"
+msgstr "sur"
+
 #, python-format
 msgid ""
 "Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d"
@@ -534,7 +545,7 @@
 
 #, python-format
 msgid "last modified: %s"
-msgstr "dernière modification : %s"
+msgstr "dernière modification : %s"
 
 msgid "match"
 msgstr "correspondance"
@@ -546,7 +557,7 @@
 msgstr "Ligne"
 
 msgid "No differences found!"
-msgstr "Aucune différence trouvée !"
+msgstr "Aucune différence trouvée !"
 
 msgid "Deletions are marked like this."
 msgstr "Texte supprimé."
@@ -561,24 +572,22 @@
 msgstr "Mot de passe"
 
 msgid "OpenID"
-msgstr ""
+msgstr "identifiant OpenID"
 
 msgid "Login"
 msgstr "Connexion"
 
-#, fuzzy
 msgid "Username"
-msgstr "Utilisateur"
-
-#, fuzzy
+msgstr "Nom d'utilisateur"
+
 msgid "Member of Groups"
-msgstr "Nombre de pages"
+msgstr "Membre des groupes"
 
 msgid "Email"
 msgstr "Adresse électronique"
 
 msgid "Jabber"
-msgstr ""
+msgstr "Jabber"
 
 msgid "Action"
 msgstr "Action"
@@ -593,31 +602,43 @@
 msgstr "Désactiver l'utilisateur"
 
 msgid "Mail account data"
-msgstr "Envoi de identifiants de connexion"
+msgstr "Envoi de vos identifiants de connexion"
 
 msgid "Missing password. Please enter user name and password."
 msgstr ""
 "Mot de passe manquant. Veuillez indiquer un nom d'utilisateur et un mot de "
 "passe."
 
-#, fuzzy
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr "Panne du serveur LDAP %(server)s."
+
 msgid "You need to log in."
-msgstr "Vous êtes maintenant déconnecté."
+msgstr "Vous devez d'abord vous connecter."
 
 #, 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 ""
+"Si vous n'avez pas de compte, <a href=\"%(userprefslink)s\">vous pouvez en "
+"créer un maintenant</a>. <a href=\"%(sendmypasswordlink)s\">Mot de passe "
+"oublié ?</a>"
 
 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 ""
+"Merci d'indiquer maintenant votre nom d'utilisateur\n"
+"Si vous choisissez un compte existant le mot de mot de passe\n"
+"vous sera demandé afin de l'associer avec l'identifiant OpenID."
 
 msgid "Choose this name"
-msgstr ""
+msgstr "Choisir ce nom"
+
+msgid "This is not a valid username, choose a different one."
+msgstr "Ce nom d'utilisateur n'est pas valide, choisissez-en un autre."
 
 msgid ""
 "The username you have chosen is already\n"
@@ -625,55 +646,59 @@
 "the username with your OpenID. Otherwise, please choose a different\n"
 "username and leave the password field blank."
 msgstr ""
+"Le nom d'utilisateur que vous avez choisi est déjà utilisé\n"
+"S'il s'agit de votre nom d'utilisateur, entrez votre mot de passe\n"
+"ci-dessous pour l'associer avec votre identifiant OpenID. Sinon\n"
+"merci de choisir un autre nom d'utilisateur et de laisser le\n"
+"champ du mot de passe vide."
 
 msgid "Associate this name"
-msgstr ""
+msgstr "Associer à ce nom"
 
 #, python-format
 msgid "OpenID error: %s."
-msgstr ""
-
-#, fuzzy
+msgstr "Erreur OpenID: %s."
+
 msgid "Verification canceled."
-msgstr "L'opération a été annulée."
+msgstr "La vérification a été annulée."
 
 msgid "OpenID failure."
-msgstr ""
-
-msgid "This is not a valid username, choose a different one."
-msgstr ""
+msgstr "Échec OpenID."
 
 msgid "Your account is now associated to your OpenID."
-msgstr ""
+msgstr "Votre compte est maintenant associé à votre identifiant OpenID."
 
 msgid "The password you entered is not valid."
-msgstr ""
+msgstr "Le mot de passe entré est incorrect."
 
 msgid "OpenID verification requires that you click this button:"
-msgstr ""
+msgstr "Veuillez cliquer sur ce bouton pour l'identification OpenID:"
 
 msgid "Anonymous sessions need to be enabled for OpenID login."
 msgstr ""
+"L'identification OpenID nécessite que les sessions anonymes soient activées."
 
 msgid "Failed to resolve OpenID."
-msgstr ""
+msgstr "Échec de la résolution de l'identifiant OpenID."
 
 msgid "OpenID discovery failure, not a valid OpenID."
-msgstr ""
+msgstr "Échec de la découverte OpenID, l'identifiant OpenID est incorrect."
 
 msgid "No OpenID."
-msgstr ""
+msgstr "Pas d'identifiant OpenID."
 
 msgid ""
 "If you do not have an account yet, you can still log in with your OpenID and "
 "create one during login."
 msgstr ""
+"Si vous n'avez pas encore de compte, vous pouvez vous connecter avec\n"
+"votre identifiant OpenID et créer un compte à partir de là."
 
 msgid "Failed to connect to database."
-msgstr ""
+msgstr "Échec de la connexion à la base de donnée."
 
 msgid "Could not contact botbouncer.com."
-msgstr ""
+msgstr "Impossible de contacter botbouncer.com."
 
 msgid "Wiki"
 msgstr "Wiki"
@@ -748,7 +773,7 @@
 msgstr "Cliquez ici pour faire une recherche de ce titre dans les pages"
 
 msgid "Settings"
-msgstr ""
+msgstr "Paramètres"
 
 msgid "Logout"
 msgstr "Déconnexion"
@@ -764,9 +789,9 @@
 msgid "last modified %(time)s"
 msgstr "dernière modification %(time)s"
 
-# Il faut utiliser une espace normale après « Rechercher »,
+# Il faut utiliser une espace normale après « Rechercher »,
 # sinon, du fait d'un défaut du moteur de Mozilla, le texte
-# « Rechercher » ne s'efface pas de la barre de recherche.
+# « Rechercher » ne s'efface pas de la barre de recherche.
 msgid "Search:"
 msgstr "Rechercher :"
 
@@ -780,7 +805,7 @@
 msgstr "Rechercher"
 
 msgid "More Actions:"
-msgstr "Autres actions :"
+msgstr "Autres actions :"
 
 msgid "------------------------"
 msgstr "------------------------"
@@ -869,7 +894,7 @@
 msgstr "(mémorisé dans le cache le %s)"
 
 msgid "Or try one of these actions:"
-msgstr "Ou essayez l'une de ces actions :"
+msgstr "Ou essayez l'une de ces actions :"
 
 msgid "Date"
 msgstr "Date"
@@ -886,7 +911,7 @@
 #, python-format
 msgid "Connection to mailserver '%(server)s' failed: %(reason)s"
 msgstr ""
-"La connexion au serveur de messagerie « %(server)s » a échouée : %(reason)s"
+"La connexion au serveur de messagerie « %(server)s » a échouée : %(reason)s"
 
 msgid "Mail not sent"
 msgstr "Courrier non envoyé"
@@ -905,12 +930,12 @@
 
 #, python-format
 msgid "**You are not allowed to read the page: %s**"
-msgstr "** Vous n'êtes pas autorisé à lire la page : %s ! **"
+msgstr "** Vous n'êtes pas autorisé à lire la page : %s ! **"
 
 #, python-format
 msgid "**Could not find the referenced page: %s**"
 msgstr ""
-"**Impossible de trouver la page à laquelle il est fait référence : %s**"
+"**Impossible de trouver la page à laquelle il est fait référence : %s**"
 
 msgid "XSLT option disabled, please look at HelpOnConfiguration."
 msgstr "L'option XSLT a été désactivée, consultez l'AideDeParamétrage."
@@ -922,24 +947,24 @@
 
 #, python-format
 msgid "%(errortype)s processing error"
-msgstr "Erreur d'exécution : %(errortype)s"
+msgstr "Erreur d'exécution : %(errortype)s"
 
 #, python-format
 msgid "Expected \"%(wanted)s\" after \"%(key)s\", got \"%(token)s\""
 msgstr ""
-"« %(wanted)s » était attendu après « %(key)s », mais « %(token)s » a été obtenu"
+"« %(wanted)s » était attendu après « %(key)s », mais « %(token)s » a été obtenu"
 
 #, python-format
 msgid "Expected an integer \"%(key)s\" before \"%(token)s\""
-msgstr "Un entier « %(key)s » était attendu avant « %(token)s »"
+msgstr "Un entier « %(key)s » était attendu avant « %(token)s »"
 
 #, python-format
 msgid "Expected an integer \"%(arg)s\" after \"%(key)s\""
-msgstr "Un entier « %(arg)s » était attendu après « %(key)s »"
+msgstr "Un entier « %(arg)s » était attendu après « %(key)s »"
 
 #, python-format
 msgid "Expected a color value \"%(arg)s\" after \"%(key)s\""
-msgstr "Une couleur « %(arg)s » était attendue après « %(key)s »"
+msgstr "Une couleur « %(arg)s » était attendue après « %(key)s »"
 
 msgid "FrontPage"
 msgstr "PageD'Accueil"
@@ -1014,124 +1039,128 @@
 msgstr "Orthographe"
 
 msgid "Discussion"
-msgstr ""
+msgstr "Discussion"
 
 #, python-format
 msgid ""
 "Sorry, can not save page because \"%(content)s\" is not allowed in this wiki."
 msgstr ""
-"Désolé, impossible d'enregistrer la page car « %(content)s » n'est pas "
+"Désolé, impossible d'enregistrer la page car « %(content)s » n'est pas "
 "autorisé sur ce wiki."
 
 msgid "Notification"
-msgstr ""
+msgstr "Notification"
 
 msgid "Notification settings saved!"
-msgstr ""
-
-#, fuzzy
+msgstr "Paramètres de notification sauvegardés !"
+
 msgid "'''Email'''"
-msgstr "Adresse électronique"
+msgstr "'''Adresse électronique'''"
 
 msgid "'''Jabber'''"
-msgstr ""
+msgstr "'''Jabber'''"
 
 msgid "'''Event type'''"
-msgstr ""
+msgstr "'''Type d'évènement'''"
 
 msgid "Select the events you want to be notified about."
-msgstr ""
+msgstr "Sélectionnez les évènement dont vous souhaitez être averti :"
 
 msgid ""
 "Before you can be notified, you need to provide a way to contact you in the "
 "general preferences."
 msgstr ""
-
-#, fuzzy
+"Avant de pouvoir être averti, vous devez donner un moyen de vous "
+"contacterdans les préférences générales"
+
 msgid "Subscribed events"
-msgstr "Abonner un utilisateur"
-
-#, fuzzy
+msgstr "Évènements auxquels vous êtes abonnés"
+
 msgid "Subscribed wiki pages<<BR>>(one regex per line)"
-msgstr "Abonnements (une expression rationnelle par ligne)"
+msgstr ""
+"Pages du wiki auxquelles vous êtes abonnés<<BR>>(une expression rationnelle "
+"par ligne)"
 
 msgid "Save"
 msgstr "Enregistrer"
 
-#, fuzzy
 msgid "Change password"
-msgstr "Mot de passe"
+msgstr "Modifier le mot de passe"
 
 msgid "Passwords don't match!"
-msgstr "Les mots de passe ne correspondent pas !"
+msgstr "Les mots de passe ne correspondent pas !"
 
 msgid "Please specify a password!"
-msgstr "Veuillez indiquer un mot de passe !"
+msgstr "Veuillez indiquer un mot de passe !"
 
 #, python-format
 msgid "Password not acceptable: %s"
-msgstr "Mot de passe non acceptable : %s"
+msgstr "Mot de passe non acceptable : %s"
 
 msgid "Your password has been changed."
-msgstr ""
+msgstr "Votre mot de passe a été changé."
 
 msgid "To change your password, enter a new password twice."
 msgstr ""
+"Pour changer votre mot de passe, entrez deux fois un nouveau mot de passe."
 
 msgid "Password repeat"
 msgstr "Répétez le mot de passe"
 
 msgid "Switch user"
-msgstr ""
+msgstr "Changer d'utilisateur"
 
 msgid "No user selected"
-msgstr ""
+msgstr "Pas d'utilisateur sélectionné"
 
 msgid ""
 "You can now change the settings of the selected user account; log out to get "
 "back to your account."
 msgstr ""
+"Vous pouvez changer les réglages du compte utilisateur sélectionné;"
+"déconnectez-vous pour retourner à votre compte."
 
 msgid "You are the only user."
-msgstr ""
+msgstr "Vous êtes le seul utilisateur."
 
 msgid ""
 "As a superuser, you can temporarily assume the identity of another user."
 msgstr ""
+"En tant que superutilisateur, vous pouvez vous connecter temporairement sous "
+"l'identité d'un autre utilisateur."
 
 msgid "Select User"
 msgstr "Choix du compte"
 
 msgid "OpenID settings"
-msgstr ""
+msgstr "Paramètres OpenID"
 
 msgid "Cannot remove all OpenIDs."
-msgstr ""
+msgstr "Impossible d'enlever tous les identifiants OpenID."
 
 msgid "The selected OpenIDs have been removed."
-msgstr ""
+msgstr "Les identifiants OpenID sélectionnés ont été enlevés."
 
 msgid "No OpenID given."
-msgstr ""
+msgstr "Pas d'identifiant OpenID entré."
 
 msgid "OpenID is already present."
-msgstr ""
-
-#, fuzzy
+msgstr "OpenID est déjà présent."
+
 msgid "This OpenID is already used for another account."
-msgstr "Cette page a déjà été supprimée ou n'a jamais existé !"
+msgstr "Cet identifiant OpenID est déjà utilisé par un autre compte."
 
 msgid "OpenID added successfully."
-msgstr ""
+msgstr "Identifiant OpenID ajouté avec succès."
 
 msgid "Current OpenIDs"
-msgstr ""
+msgstr "Identifiants OpenID actuels"
 
 msgid "Remove selected"
-msgstr ""
+msgstr "Enlever les sélectionnés"
 
 msgid "Add OpenID"
-msgstr ""
+msgstr "Ajouter un identifiant OpenID"
 
 msgid "Preferences"
 msgstr "Préférences"
@@ -1142,10 +1171,10 @@
 "Name may contain any Unicode alpha numeric character, with optional one\n"
 "space between words. Group page name is not allowed."
 msgstr ""
-"Non d'utilisateur non valide « {{{%s}}} ».\n"
-"Les noms d'utilisateurs peuvent inclure tout caractère Unicode\n"
-"alphanumérique, avec éventuellement un espace entre les mots.\n"
-"Les noms des pages des groupes sont interdits."
+"Non d'utilisateur incorrect « {{{%s}}} ».\n"
+"Les noms d'utilisateurs peuvent contenir tout caractère Unicode\n"
+"alphanumérique, avec éventuellement un espace entre chaque mot.\n"
+"Les noms des pages correspondant aux groupes sont interdits."
 
 msgid "This user name already belongs to somebody else."
 msgstr "Ce nom d'utilisateur appartient déjà à quelqu'un d'autre."
@@ -1166,16 +1195,15 @@
 msgid "This email already belongs to somebody else."
 msgstr "Cette adresse électronique appartient déjà à quelqu'un d'autre."
 
-#, fuzzy
 msgid "This jabber id already belongs to somebody else."
-msgstr "Cette adresse électronique appartient déjà à quelqu'un d'autre."
+msgstr "Cet identifiant Jabber appartient déjà à quelqu'un d'autre."
 
 #, python-format
 msgid "The theme '%(theme_name)s' could not be loaded!"
-msgstr "Le thème « %(theme_name)s » n'a pu être chargé !"
+msgstr "Le thème « %(theme_name)s » n'a pu être chargé !"
 
 msgid "User preferences saved!"
-msgstr "Vos préférences utilisateur ont été enregistrées !"
+msgstr "Vos préférences utilisateur ont été enregistrées !"
 
 msgid "Default"
 msgstr "par défaut"
@@ -1219,7 +1247,18 @@
 msgid "Quick links"
 msgstr "Liens rapides"
 
-#, fuzzy, python-format
+#, fuzzy
+msgid "OpenID server"
+msgstr "OpenID n'est pas proposé"
+
+#, fuzzy
+msgid "The selected websites have been removed."
+msgstr "Les identifiants OpenID sélectionnés ont été enlevés."
+
+msgid "Trusted websites"
+msgstr ""
+
+#, python-format
 msgid ""
 "Dear Wiki user,\n"
 "\n"
@@ -1228,21 +1267,20 @@
 "\n"
 "The \"%(pagename)s\" page has been changed by %(editor)s:\n"
 msgstr ""
-"Cher utilisateur du Wiki,\n"
+"Cher utilisateur du wiki,\n"
 "\n"
-"vous vous êtes abonné aux notifications de changements de la page wiki ou de "
-"la catégorie wiki de « %(sitename)s ».\n"
+"vous vous êtes abonné aux notifications des modifications d'une page ou "
+"d'une catégorie du wiki « %(sitename)s ».\n"
 "\n"
-"La page suivante a été modifiée par %(editor)s :\n"
-"%(pagelink)s\n"
+"La page « %(pagename)s » a été modifiée par %(editor)s :\n"
 
 msgid "New page:\n"
-msgstr "Nouvelle page :\n"
+msgstr "Nouvelle page :\n"
 
 msgid "No differences found!\n"
-msgstr "Aucune différence n'a été trouvée !\n"
-
-#, fuzzy, python-format
+msgstr "Aucune différence n'a été trouvée !\n"
+
+#, python-format
 msgid ""
 "Dear wiki user,\n"
 "\n"
@@ -1252,15 +1290,15 @@
 "The page \"%(pagename)s\" has been deleted by %(editor)s:\n"
 "\n"
 msgstr ""
-"Cher utilisateur du Wiki,\n"
+"Cher utilisateur du wiki,\n"
 "\n"
-"vous vous êtes abonné aux notifications de changements de la page wiki ou de "
-"la catégorie wiki de « %(sitename)s ».\n"
+"vous vous êtes abonné aux notifications des modifications d'une page ou "
+"d'une catégorie du wiki « %(sitename)s ».\n"
 "\n"
-"La page suivante a été modifiée par %(editor)s :\n"
-"%(pagelink)s\n"
-
-#, fuzzy, python-format
+"La page « %(pagename)s » a été supprimée par %(editor)s :\n"
+"\n"
+
+#, python-format
 msgid ""
 "Dear wiki user,\n"
 "\n"
@@ -1270,17 +1308,16 @@
 "The page \"%(pagename)s\" has been renamed from \"%(oldname)s\" by %(editor)"
 "s:\n"
 msgstr ""
-"Cher utilisateur du Wiki,\n"
+"Cher utilisateur du wiki,\n"
 "\n"
-"vous vous êtes abonné aux notifications de changements de la page wiki ou de "
-"la catégorie wiki de « %(sitename)s ».\n"
+"vous vous êtes abonné aux notifications des modifications d'une page\n"
+"du wiki « %(sitename)s ».\n"
 "\n"
-"La page suivante a été modifiée par %(editor)s :\n"
-"%(pagelink)s\n"
+"La page « %(oldname)s » a été renommée « %(pagename)s » par %(editor)s :\n"
 
 #, python-format
 msgid "New user account created on %(sitename)s"
-msgstr ""
+msgstr "Nouveau compte utilisateur créé sur %(sitename)s"
 
 #, python-format
 msgid ""
@@ -1289,10 +1326,17 @@
 "    User name: %(username)s\n"
 "    Email address: %(useremail)s"
 msgstr ""
-
-#, fuzzy, python-format
+"Cher super-utilisateur, un nouveau compte vient d'être créé, dont voici les "
+"détails:\n"
+"\n"
+"    Nom d'utilisateur: %(username)s\n"
+"    Adresse de courrier électronique: %(useremail)s"
+
+#, python-format
 msgid "New attachment added to page %(pagename)s on %(sitename)s"
-msgstr "<<Verbatim(attachment:)>> %(filename)s de %(pagename)s"
+msgstr ""
+"Une nouvelle pièce jointe a été ajoutée à la page %(pagename)s de %(sitename)"
+"s"
 
 #, python-format
 msgid ""
@@ -1305,43 +1349,46 @@
 "Attachment name: %(attach_name)s\n"
 "Attachment size: %(attach_size)s\n"
 msgstr ""
+"Cher utilisateur du Wiki,\n"
+"\n"
+"vous vous êtes abonné aux notifications de changements pour la page \"%"
+"(page_name)s\".Une pièce jointe vient d'y être ajouté par %(editor)s. "
+"Quelques détails sur la pièce jointe:\n"
+"\n"
+"Nom: %(attach_name)s\n"
+"Taille: %(attach_size)s\n"
 
 msgid "Page has been modified"
-msgstr ""
+msgstr "La page a été modifiée"
 
 msgid "Page has been modified in a trivial fashion"
-msgstr ""
-
-#, fuzzy
+msgstr "Une modification mineure a été apportée à la page"
+
 msgid "Page has been renamed"
-msgstr "Nombre de consultations et de modifications"
-
-#, fuzzy
+msgstr "La page a été renommée"
+
 msgid "Page has been deleted"
-msgstr "Page « %s » supprimée avec succès !"
-
-#, fuzzy
+msgstr "La page a été supprimée"
+
 msgid "Page has been copied"
-msgstr "Nombre de consultations et de modifications"
-
-#, fuzzy
+msgstr "La page a été copiée"
+
 msgid "A new attachment has been added"
-msgstr "Nom de la pièce jointe"
+msgstr "Une nouvelle pièce jointe a été ajoutée"
 
 msgid "A page has been reverted to a previous state"
-msgstr ""
-
-#, fuzzy
+msgstr "Une version antérieure d'une page a été restaurée"
+
 msgid "A user has subscribed to a page"
-msgstr "Vous avez été abonné à cette page."
+msgstr "Un utilisateur s'est abonné à une page"
 
 msgid "A new account has been created"
-msgstr ""
+msgstr "Un nouveau compte vient d'être créé"
 
 #, python-format
 msgid "[%(sitename)s] %(trivial)sUpdate of \"%(pagename)s\" by %(username)s"
 msgstr ""
-"[%(sitename)s] Mise à jour %(trivial)sde « %(pagename)s » par %(username)s"
+"[%(sitename)s] Mise à jour %(trivial)sde « %(pagename)s » par %(username)s"
 
 msgid "Trivial "
 msgstr "mineure "
@@ -1351,43 +1398,33 @@
 "Attachment link: %(attach)s\n"
 "Page link: %(page)s\n"
 msgstr ""
-
-#, python-format
-msgid "XML RPC error: %s"
-msgstr ""
-
-#, python-format
-msgid "Low-level communication error: %s"
-msgstr ""
-
-#, fuzzy
+"Lien vers la pièce jointe: %(attach)s\n"
+"Lien vers une page: %(page)s\n"
+
 msgid "Attachment link"
-msgstr "Pièces jointes"
-
-#, fuzzy
+msgstr "Lien vers une pièce jointe"
+
 msgid "Page link"
-msgstr "Pages similaires à « %s »"
-
-#, fuzzy
+msgstr "Liens vers une page"
+
 msgid "Changed page"
-msgstr "Pages du paquet"
-
-#, fuzzy
+msgstr "Page modifiée"
+
 msgid "Page changed"
-msgstr "Enregistrer les modifications"
+msgstr "La page a été modifiée"
 
 msgid "Options --pages and --search are mutually exclusive!"
-msgstr "Les options --pages et --search sont mutuellement exclusives !"
+msgstr "Les options --pages et --search sont mutuellement exclusives !"
 
 msgid "You must specify an output file!"
-msgstr "Vous devez indiquer un fichier de sortie !"
+msgstr "Vous devez indiquer un fichier de sortie !"
 
 msgid "No pages specified using --pages or --search, assuming full package."
 msgstr ""
 "Aucune page indiquée via --pages ou --search, on prend le paquet complet."
 
 msgid "Output file already exists! Cowardly refusing to continue!"
-msgstr "Le fichier de sortie existe déjà ! Abandon lâche de l'opération."
+msgstr "Le fichier de sortie existe déjà ! Abandon lâche de l'opération."
 
 msgid "Language"
 msgstr "Langue"
@@ -1396,7 +1433,7 @@
 msgstr "Autres"
 
 msgid "Charts are not available!"
-msgstr "Les graphiques ne sont pas disponibles !"
+msgstr "Les graphiques ne sont pas disponibles !"
 
 msgid "Page Size Distribution"
 msgstr "Page Taille Répartition"
@@ -1408,10 +1445,10 @@
 msgstr "Nombre de pages de cette taille"
 
 msgid "Views/day"
-msgstr "Consultations / jour"
+msgstr "Consultations / jour"
 
 msgid "Edits/day"
-msgstr "Modifications / jour"
+msgstr "Modifications / jour"
 
 msgid "Page hits and edits"
 msgstr "Nombre de consultations et de modifications"
@@ -1439,7 +1476,6 @@
 msgid "Distribution of User-Agent Types"
 msgstr "Répartition des types d'agents utilisateurs"
 
-#, fuzzy
 msgid ""
 " Emphasis:: <<Verbatim('')>>''italics''<<Verbatim('')>>; <<Verbatim(''')"
 ">>'''bold'''<<Verbatim(''')>>; <<Verbatim(''''')>>'''''bold "
@@ -1457,27 +1493,23 @@
 "\n"
 "(!) For more help, see HelpOnEditing or SyntaxReference.\n"
 msgstr ""
-" Mise en relief:: <<Verbatim('')>>''italique''<<Verbatim('')>> ; <<Verbatim"
-"(''')>>'''gras'''<<Verbatim(''')>> ; <<Verbatim(''''')>>'''''gras "
-"italique'''''<<Verbatim(''''')>> ; <<Verbatim('')>>''mélange de ''<<Verbatim"
-"(''')>>'''''gras'''<<Verbatim(''')>> et d'italique''<<Verbatim('')>> ; "
+" Mise en relief:: <<Verbatim('')>>''italique''<<Verbatim('')>> ; <<Verbatim"
+"(''')>>'''gras'''<<Verbatim(''')>> ; <<Verbatim(''''')>>'''''gras "
+"italique'''''<<Verbatim(''''')>> ; <<Verbatim('')>>''mélange de ''<<Verbatim"
+"(''')>>'''''gras'''<<Verbatim(''')>> et d'italique''<<Verbatim('')>> ; "
 "<<Verbatim(----)>> filet horizontal.\n"
-" Titres:: <<Verbatim(=)>> Titre 1 <<Verbatim(=)>> ; <<Verbatim(==)>> Titre 2 "
-"<<Verbatim(==)>> ; <<Verbatim(===)>> Titre 3 <<Verbatim(===)>> ;   <<Verbatim"
-"(====)>> Titre 4 <<Verbatim(====)>> ; <<Verbatim(=====)>> Titre 5 <<Verbatim"
-"(=====)>>.\n"
-" Listes:: une espace suivi de : « * » puces ; « 1. », « a. », « A. », « i.  », "
-"« I. » énumérations ; « 1.#n » énumération commençant à n ; une espace seule "
-"ajoute une indentation.\n"
-" Liens:: <<Verbatim(MotsAccolésDébutantParDesMajuscules)>> ; <<Verbatim"
+" Titres:: = Titre 1 = ; == Titre 2 == ; === Titre 3 === ; ==== Titre 4 "
+"==== ; ===== Titre 5 =====.\n"
+" Listes:: une espace suivi de : * puces ; 1., a., A., i., I. énumérations ; "
+"1.#n énumération commençant à n ; une espace seule ajoute une indentation.\n"
+" Liens:: <<Verbatim(MotsAccolésDébutantParDesMajuscules)>> ; <<Verbatim"
 "([[cible|intitulé]])>>.\n"
-" Tables:: || texte de la cellule |||| texte s'étendant sur 2 colonnes || ; "
+" Tables:: || texte de la cellule |||| texte s'étendant sur 2 colonnes || ; "
 "aucune espace ne doit suivre la fin d'un tableau ou d'un titre.\n"
 "\n"
 "(!) Pour plus d'informations, reportez-vous à l'[AideDeL'Édition|"
 "AideDeL'Édition] ou au RésuméDeLaSyntaxe.\n"
 
-#, fuzzy
 msgid ""
 "{{{\n"
 "Emphasis: *italic* **bold** ``monospace``\n"
@@ -1498,25 +1530,24 @@
 "reStructuredText Quick Reference]].\n"
 msgstr ""
 "{{{\n"
-"Mise en relief : *italique* **gras** ``à chasse fixe``<br/>\n"
+"Mise en relief : *italique* **gras** ``à chasse fixe``\n"
 "\n"
-"Titres : Titre 1  Titre 2  Titre 3\n"
+"Titres : Titre 1  Titre 2  Titre 3\n"
 "         =======  -------  ~~~~~~~\n"
 "\n"
-"Filet horizontal : ---- \n"
+"Filet horizontal : ---- \n"
 "\n"
-"Liens : SoulignéFinal_ `plusieurs mots encadrés d'accents graves`_ "
+"Liens : SoulignéFinal_ `plusieurs mots encadrés d'accents graves`_ "
 "externe_ \n"
 "\n"
-".. _externe : http://exemple.fr.invalid/blabla/\n"
+".. _externe : http://exemple.fr.invalid/blabla/\n"
 "\n"
-"Listes : * puces ; 1., a. numérotées.\n"
+"Listes : * puces ; 1., a. énumérations.\n"
 "}}}\n"
 "(!) Pour en savoir plus, consultez le \n"
 "[[http://docutils.sourceforge.net/docs/user/rst/quickref.html|Guide de "
 "référence rapide de reStructuredText]] (en anglais).\n"
 
-#, fuzzy
 msgid ""
 " Emphasis:: <<Verbatim(//)>>''italics''<<Verbatim(//)>>; <<Verbatim(**)"
 ">>'''bold'''<<Verbatim(**)>>; <<Verbatim(**//)>>'''''bold "
@@ -1533,25 +1564,21 @@
 "\n"
 "(!) For more help, see HelpOnEditing or HelpOnCreoleSyntax.\n"
 msgstr ""
-" Mise en relief:: <<Verbatim('')>>''italique''<<Verbatim('')>> ; <<Verbatim"
-"(''')>>'''gras'''<<Verbatim(''')>> ; <<Verbatim(''''')>>'''''gras "
-"italique'''''<<Verbatim(''''')>> ; <<Verbatim('')>>''mélange de ''<<Verbatim"
-"(''')>>'''''gras'''<<Verbatim(''')>> et d'italique''<<Verbatim('')>> ; "
-"<<Verbatim(----)>> filet horizontal.\n"
-" Titres:: <<Verbatim(=)>> Titre 1 <<Verbatim(=)>> ; <<Verbatim(==)>> Titre 2 "
-"<<Verbatim(==)>> ; <<Verbatim(===)>> Titre 3 <<Verbatim(===)>> ;   <<Verbatim"
-"(====)>> Titre 4 <<Verbatim(====)>> ; <<Verbatim(=====)>> Titre 5 <<Verbatim"
-"(=====)>>.\n"
-" Listes:: une espace suivi de : « * » puces ; « 1. », « a. », « A. », « i.  », "
-"« I. » énumérations ; « 1.#n » énumération commençant à n ; une espace seule "
-"ajoute une indentation.\n"
-" Liens:: <<Verbatim(MotsAccolésDébutantParDesMajuscules)>> ; <<Verbatim"
-"([[cible|intitulé]])>>.\n"
-" Tables:: || texte de la cellule |||| texte s'étendant sur 2 colonnes || ; "
-"aucune espace ne doit suivre la fin d'un tableau ou d'un titre.\n"
+" Mise en relief:: <<Verbatim(//)>>''italique''<<Verbatim(//)>> ; <<Verbatim"
+"(**)>>'''gras'''<<Verbatim(**)>> ; <<Verbatim(**//)>>'''''gras "
+"italique'''''<<Verbatim(//**)>> ; <<Verbatim(//)>>''mélange de ''<<Verbatim"
+"(**)>>'''''gras'''<<Verbatim(**)>> et d'italique''<<Verbatim(//)>> ;\n"
+" <<Verbatim(----)>> filet horizontal.\n"
+" Forcer un passage à la ligne:: <<Verbatim(\\\\)>>\n"
+" Titres:: = Titre 1 = ; == Titre 2 == ; === Titre 3 === ; ==== Titre 4 "
+"==== ; ===== Titre 5 =====.\n"
+" Listes:: * puces ; ** puces secondaires ; # énumérations ; ## énumérations "
+"secondaires.\n"
+" Liens:: <<Verbatim([[cible]])>> ; <<Verbatim([[cible|intitulé]])>>.\n"
+" Tables:: |= texte d'en-tête | texte d'une cellule | texte d'une autre "
+"cellule | ;\n"
 "\n"
-"(!) Pour plus d'informations, reportez-vous à l'[AideDeL'Édition|"
-"AideDeL'Édition] ou au RésuméDeLaSyntaxe.\n"
+"(!) Pour plus d'informations, reportez-vous à l'AideDeLaSyntaxeCréole.\n"
 
 msgid "UnSubscribe"
 msgstr "SeDésabonner"
@@ -1562,10 +1589,10 @@
 "les informations sur l'auteur"
 
 msgid "Open editor on double click"
-msgstr "Double-cliquer pour ouvrir l'éditeur"
+msgstr "Un double-clic ouvre l'éditeur"
 
 msgid "After login, jump to last visited page"
-msgstr "À la connexion, démarrer à la dernière page visitée"
+msgstr "À la connexion, démarrer sur la dernière page visitée"
 
 msgid "Show comment sections"
 msgstr "Afficher les commentaires"
@@ -1581,7 +1608,7 @@
 msgstr "Voir la barre d'icônes"
 
 msgid "Show top/bottom links in headings"
-msgstr "Ajouter des liens vers le haut et le bas de la page aux titres"
+msgstr "Ajouter aux titres des liens vers les haut et bas de la page"
 
 msgid "Show fancy diffs"
 msgstr "Affichage amélioré des modifications"
@@ -1602,7 +1629,7 @@
 msgstr "Pseudonyme"
 
 msgid "Jabber ID"
-msgstr ""
+msgstr "Indentifiant Jabber"
 
 msgid "User CSS URL"
 msgstr "URL du style CSS personnel"
@@ -1613,35 +1640,34 @@
 msgid "Editor size"
 msgstr "Taille de l'éditeur"
 
-#, fuzzy
 msgid "File attachment browser"
-msgstr "Nom de la pièce jointe"
+msgstr "Parcourrir les pièces jointes"
 
 msgid "User account browser"
-msgstr ""
+msgstr "Parcourrir les comptes utilisateurs"
 
 #, python-format
 msgid "Invalid include arguments \"%s\"!"
-msgstr "Les arguments du include ne sont pas valides : « %s » !"
+msgstr "Les arguments du include sont incorrects : « %s » !"
 
 #, python-format
 msgid "Nothing found for \"%s\"!"
-msgstr "Rien n'a été trouvé sur « %s » !"
+msgstr "Rien n'a été trouvé sur « %s » !"
 
 msgid "edit"
 msgstr "éditer"
 
 #, python-format
 msgid "Upload of attachment '%(filename)s'."
-msgstr "Ajout de la pièce jointe « %(filename)s »."
+msgstr "Ajout de la pièce jointe « %(filename)s »."
 
 #, python-format
 msgid "Attachment '%(filename)s' deleted."
-msgstr "Suppression de la pièce jointe « %(filename)s »."
+msgstr "Suppression de la pièce jointe « %(filename)s »."
 
 #, python-format
 msgid "Drawing '%(filename)s' saved."
-msgstr "Enregistrement du dessin « %(filename)s »."
+msgstr "Enregistrement du dessin « %(filename)s »."
 
 #, python-format
 msgid "Revert to revision %(rev)d."
@@ -1649,7 +1675,7 @@
 
 #, python-format
 msgid "Renamed from '%(oldpagename)s'."
-msgstr "Page créée en renommant la page « %(oldpagename)s »."
+msgstr "Page créée en renommant la page « %(oldpagename)s »."
 
 #, python-format
 msgid "%(mins)dm ago"
@@ -1660,7 +1686,7 @@
 
 #, python-format
 msgid "(currently set to %s)"
-msgstr "(ayant actuellement la valeur : %s)"
+msgstr "(ayant actuellement la valeur : %s)"
 
 msgid "Delete bookmark"
 msgstr "Supprimer le signet"
@@ -1694,7 +1720,7 @@
 
 #, python-format
 msgid "Release %s [Revision %s]"
-msgstr "Version %s [édition « %s »]"
+msgstr "Version %s [édition « %s »]"
 
 msgid "4Suite Version"
 msgstr "Version de 4Suite"
@@ -1752,30 +1778,18 @@
 msgid "index unavailable"
 msgstr "index non disponible"
 
+msgid "Xapian and/or Python Xapian bindings not installed"
+msgstr "Xapian ou l'interface Python de Xapian ne sont pas installés"
+
 msgid "N/A"
 msgstr "Non disponible"
 
-msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr "Xapian ou l'interface Python de Xapian ne sont pas installés"
-
 msgid "Xapian search"
 msgstr "Recherche Xapian"
 
-msgid "Xapian Version"
-msgstr "Version de Xapian"
-
-msgid "PyStemmer not installed"
-msgstr "PyStemmer n'est pas installé"
-
 msgid "Stemming for Xapian"
 msgstr "Recherche des racines pour Xapian"
 
-msgid "PyStemmer Version"
-msgstr "Version de PyStemmer"
-
-msgid "PyStemmer stems"
-msgstr "Racines de PyStemmer"
-
 msgid "Active threads"
 msgstr "Fils d'exécution actifs"
 
@@ -1792,7 +1806,7 @@
 msgstr "ne contenant aucun des termes suivants"
 
 msgid "last modified since (e.g. last 2 weeks)"
-msgstr "modifié depuis (p. ex. « last 2 weeks »)"
+msgstr "modifié depuis (p. ex. « last 2 weeks »)"
 
 msgid "any category"
 msgstr "n'importe quelle catégorie"
@@ -1825,7 +1839,7 @@
 msgstr "Recherche incluant toutes les révisions"
 
 msgid "Go get it!"
-msgstr "Lancer la récupération !"
+msgstr "Lancer la récupération !"
 
 #, python-format
 msgid "No quotes on %(pagename)s."
@@ -1845,39 +1859,53 @@
 
 #, python-format
 msgid "Please use a more selective search term instead of {{{\"%s\"}}}"
-msgstr "Choisissez un critère de recherche plus sélectif que « {{{%s}}} »"
+msgstr "Choisissez un critère de recherche plus sélectif que « {{{%s}}} »"
+
+#, python-format
+msgid ""
+"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
+"for more information."
+msgstr ""
+"Le texte de cette recherche, « {{{%s}}} », est incorrect. Veuillez consulter "
+"la page AideDeLaRecherche pour plus d'informations."
 
 #, python-format
 msgid "Upload new attachment \"%(filename)s\""
-msgstr "Ajout de la nouvelle pièce jointe « %(filename)s »"
+msgstr "Ajout de la nouvelle pièce jointe « %(filename)s »"
 
 #, python-format
 msgid ""
 "%(extension_name)s %(extension_type)s: Required argument %(argument_name)s "
 "missing."
 msgstr ""
+"%(extension_name)s %(extension_type)s : l'argument %(argument_name)s requis "
+"est absent."
 
 #, python-format
 msgid ""
 "%(extension_name)s %(extension_type)s: Invalid %(argument_name)s=%"
 "(argument_value)s!"
 msgstr ""
+"%(extension_name)s %(extension_type)s : %(argument_name)s=%(argument_value)s "
+"est incorrect !"
 
 #, python-format
 msgid ""
 "Current configuration does not allow embedding of the file %(file)s because "
 "of its mimetype %(mimetype)s."
 msgstr ""
+"Le paramétrage actuel ne permet pas d'intégrer à la page le fichier %(file)s "
+"du fait de son type MIME : %(mimetype)s."
 
 msgid "Embedded"
 msgstr "intégré"
 
 #, python-format
 msgid "Unsupported navigation scheme '%(scheme)s'!"
-msgstr "Schéma de navigation inconnu « %(scheme)s » !"
+msgstr "Schéma de navigation inconnu « %(scheme)s » !"
 
 msgid "No parent page found!"
-msgstr "Page supérieure non trouvée !"
+msgstr "Page supérieure non trouvée !"
 
 msgid "Slideshow"
 msgstr "Présentation"
@@ -1899,19 +1927,18 @@
 msgstr "Aucune page référencée ne reste à créer."
 
 msgid "You need to provide a chart type!"
-msgstr "Vous devez fournir un type de graphique !"
+msgstr "Vous devez fournir un type de graphique !"
 
 #, python-format
 msgid "Bad chart type \"%s\"!"
-msgstr "Type de graphique « %s » incorrect !"
-
-#, fuzzy
+msgstr "Type de graphique « %s » incorrect !"
+
 msgid "Revert"
-msgstr "Tout restaurer !"
+msgstr "Restaurer !"
 
 msgid "You are not allowed to revert this page!"
 msgstr ""
-"Vous n'êtes pas autorisé à restaurer une version antérieure de cette page !"
+"Vous n'êtes pas autorisé à restaurer une version antérieure de cette page !"
 
 msgid ""
 "You were viewing the current revision of this page when you called the "
@@ -1924,24 +1951,22 @@
 "utiliser à nouveau l'action de restauration."
 
 msgid "This page is already deleted or was never created!"
-msgstr "Cette page a déjà été supprimée ou n'a jamais existé !"
-
-#, fuzzy
+msgstr "Cette page a déjà été supprimée ou n'a jamais existé !"
+
 msgid "Optional reason for reverting this page"
-msgstr "Motif de cette copie (optionnel)"
-
-#, fuzzy
+msgstr "Raison optionnelle de cette restauration"
+
 msgid "Really revert this page?"
-msgstr "Voulez-vous vraiment renommer cette page ?"
+msgstr "Voulez-vous vraiment restaurer cette page ?"
 
 #, python-format
 msgid "Rolled back changes to the page %s."
-msgstr "Annulation des modifications apportées à la page %s"
+msgstr "Annulation des modifications apportées à la page %s."
 
 msgid "Exception while calling rollback function:"
 msgstr ""
 "Une exception est survenue lors de l'appel à la fonction d'annulation des "
-"modifications :"
+"modifications :"
 
 msgid ""
 "Please enter your password of your account at the remote wiki below. "
@@ -1971,15 +1996,14 @@
 "Refer to HelpOnSynchronisation for help."
 msgstr ""
 "Paramètres incorrects. Veuillez au moins indiquer le paramètre "
-"''remoteWiki'' (« wiki distant ») dans cette page.Consultez la page "
+"''remoteWiki'' (« wiki distant ») dans cette page.Consultez la page "
 "AideDeSynchronisation pour plus d'informations."
 
 msgid "The ''remoteWiki'' is unknown."
 msgstr "Le wiki distant (paramètre ''remoteWiki'') est inconnu."
 
-#, fuzzy
 msgid "A severe error occurred:"
-msgstr "Une erreur sévère s'est produite :"
+msgstr "Une erreur sévère s'est produite :"
 
 msgid "Synchronisation finished. Look below for the status messages."
 msgstr ""
@@ -1989,17 +2013,17 @@
 msgid "Synchronisation started -"
 msgstr "Synchronisation lancée -"
 
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "Got a list of %s local and %s remote pages. This results in %s pages to "
 "process."
 msgstr ""
 "Une liste de %s pages locales et de %s pages distantes a été obtenue. Au "
-"total, il y a %s pages différentes en tout."
+"total, il faudra traiter %s pages."
 
 #, python-format
 msgid "After filtering: %s pages"
-msgstr "Après filtrage : %s pages"
+msgstr "Après filtrage : %s pages"
 
 #, python-format
 msgid "Skipped page %s because of no write access to local page."
@@ -2013,7 +2037,7 @@
 
 #, python-format
 msgid "Error while deleting page %s locally:"
-msgstr "Erreur lors de la suppression de la page locale %s :"
+msgstr "Erreur lors de la suppression de la page locale %s :"
 
 #, python-format
 msgid "Deleted page %s remotely."
@@ -2021,7 +2045,7 @@
 
 #, python-format
 msgid "Error while deleting page %s remotely:"
-msgstr "Erreur lors de la suppression de la page distante %s :"
+msgstr "Erreur lors de la suppression de la page distante %s :"
 
 #, python-format
 msgid ""
@@ -2090,7 +2114,7 @@
 
 #, python-format
 msgid "Page %s successfully merged."
-msgstr "Page %s fusionnée avec succès !"
+msgstr "Page %s fusionnée avec succès !"
 
 #, python-format
 msgid "Page %s contains conflicts that were introduced on the remote side."
@@ -2109,13 +2133,18 @@
 "\n"
 "Once you have logged in, simply reload this page."
 msgstr ""
+"Vous devrez aller sur le wiki du fournisseur de votre identifiant OpenID\n"
+"et vous y connecter avant de pouvoir utiliser votre identifiant OpenID.\n"
+"MoinMoin ne vous permettra pas d'entrer votre mot de passe ici.\n"
+"\n"
+"Une fois connecté, rechargez simplement cette page."
 
 msgid "OpenID Trust verification"
-msgstr ""
+msgstr "Vérification de la confiance OpenID"
 
 #, python-format
 msgid "The site %s has asked for your identity."
-msgstr ""
+msgstr "Le site %s a demandé votre identité."
 
 #, python-format
 msgid ""
@@ -2125,28 +2154,34 @@
 "identity, the site will take care of reversing the\n"
 "delegation on its own.)"
 msgstr ""
+"\n"
+"Si vous acceptez, le site correspondant à la racine de confiance ci-dessous\n"
+"sera informé que vous contrôlez l'identité dont l'URL est %s (si vous\n"
+"utilisez une identité déléguée, le site se chargera de lui-même de\n"
+"remonter la délégation)."
 
 msgid "Trust root"
-msgstr ""
+msgstr "Racine de confiance"
 
 msgid "Identity URL"
-msgstr ""
-
-#, fuzzy
+msgstr "URL d'identification"
+
 msgid "Remember decision"
-msgstr "Version de PyStemmer"
+msgstr "Se rappeler de cette décision"
 
 msgid "Remember this trust decision and don't ask again"
 msgstr ""
+"Se rappeler de cette décision quant à la confiance et ne plus poser la "
+"question"
 
 msgid "Approve"
-msgstr ""
+msgstr "Approuver"
 
 msgid "Don't approve"
-msgstr ""
+msgstr "Ne pas approuver"
 
 msgid "OpenID not served"
-msgstr ""
+msgstr "OpenID n'est pas proposé"
 
 msgid ""
 "\n"
@@ -2155,15 +2190,21 @@
 "and then reload this page or click the button below to cancel this\n"
 "verification."
 msgstr ""
+"\n"
+"Malheureusement vous n'avez pas encore créé votre page personnelle "
+"(homepage).\n"
+"En conséquence nous ne pouvons vous délivrer un identifiant OpenID.\n"
+"Merci de créer votre page personnelle puis de recharger cette page,\n"
+"ou cliquez sur le bouton ci-dessous pour annuler cette vérification."
 
 #, python-format
 msgid "Invalid filename \"%s\"!"
-msgstr "Nom de fichier non valide : « %s » !"
+msgstr "Nom de fichier incorrect : « %s » !"
 
 #, python-format
 msgid "Attachment '%(target)s' (remote name '%(filename)s') already exists."
 msgstr ""
-"La pièce jointe « %(target)s » (nom distant « %(filename)s ») existe déjà."
+"La pièce jointe « %(target)s » (nom distant « %(filename)s ») existe déjà."
 
 #, python-format
 msgid "Created the package %s containing the pages %s."
@@ -2180,14 +2221,14 @@
 
 #, python-format
 msgid "No pages like \"%s\"!"
-msgstr "Aucune page n'est similaire à « %s » !"
+msgstr "Aucune page n'est similaire à « %s » !"
 
 msgid "Please choose:"
-msgstr ""
-
-#, fuzzy, python-format
+msgstr "Choisissez :"
+
+#, python-format
 msgid "You must login to use this action: %(action)s."
-msgstr "Vous devez être identifié pour utiliser l'abonnement."
+msgstr "Vous devez être identifié pour utiliser l'action %(action)s."
 
 msgid "You must login to remove a quicklink."
 msgstr "Vous devez être identifié pour supprimer un lien rapide."
@@ -2207,13 +2248,13 @@
 "Restored Backup: %(filename)s to target dir: %(targetdir)s.\n"
 "Files: %(filecount)d, Directories: %(dircount)d"
 msgstr ""
-"Sauvegarde restaurée : %(filename)s dans le répertoire : %(targetdir)s.\n"
-"Fichiers : %(filecount)d ; répertoires : %(dircount)d"
+"Sauvegarde restaurée : %(filename)s dans le répertoire : %(targetdir)s.\n"
+"Fichiers : %(filecount)d ; répertoires : %(dircount)d"
 
 #, python-format
 msgid "Restoring backup: %(filename)s to target dir: %(targetdir)s failed."
 msgstr ""
-"Restauration de sauvegarde : échec de la restauration de %(filename)s vers %"
+"Restauration de sauvegarde : échec de la restauration de %(filename)s vers %"
 "(targetdir)s."
 
 msgid "Wiki Backup / Restore"
@@ -2236,8 +2277,8 @@
 "complete.\n"
 "\n"
 msgstr ""
-"Quelques astuces :\n"
-" * Pour restaurer une sauvegarde :\n"
+"Quelques astuces :\n"
+" * Pour restaurer une sauvegarde :\n"
 "  * Restaurer une sauvegarde effacera les données existantes. Soyez "
 "prudents.\n"
 "  * Renommez-la en <identifiant_site>.tar.<compression> (enlevez les --date--"
@@ -2264,18 +2305,18 @@
 
 #, python-format
 msgid "Unknown backup subaction: %s."
-msgstr "Sous-action de sauvegarde inconnue : %s."
+msgstr "Sous-action de sauvegarde inconnue : %s."
 
 msgid "Do it."
 msgstr "Exécuter."
 
 #, python-format
 msgid "Execute action %(actionname)s?"
-msgstr "Exécuter l'action %(actionname)s ?"
+msgstr "Exécuter l'action %(actionname)s ?"
 
 #, python-format
 msgid "Action %(actionname)s is excluded in this wiki!"
-msgstr "L'action %(actionname)s est interdite sur ce wiki !"
+msgstr "L'action %(actionname)s est interdite sur ce wiki !"
 
 #, python-format
 msgid "You are not allowed to use action %(actionname)s on this page!"
@@ -2285,7 +2326,7 @@
 #, python-format
 msgid "Please use the interactive user interface to use action %(actionname)s!"
 msgstr ""
-"Utilisez l'interface interactive pour exécuter l'action %(actionname)s !"
+"Utilisez l'interface interactive pour exécuter l'action %(actionname)s !"
 
 msgid "Please log in first."
 msgstr "Veuillez d'abord vous identifier."
@@ -2293,7 +2334,7 @@
 msgid "Please first create a homepage before creating additional pages."
 msgstr "Veuillez créer une page personnelle avant de créer d'autres pages."
 
-#, python-format
+#, fuzzy, python-format
 msgid ""
 "You can add some additional sub pages to your already existing homepage "
 "here.\n"
@@ -2313,9 +2354,9 @@
 "||'''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"
+"[[%(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"
 "\n"
@@ -2336,8 +2377,8 @@
 "Utilisez le ModèleDeGroupeDePagePersonnelle lors de la création des pages\n"
 "correspondant aux groupes.\n"
 "\n"
-"||'''Ajout d'une nouvelle page personnelle :'''||'''Groupes utilisés pour "
-"les listes de contrôle d'accès :'''||\n"
+"||'''Ajout d'une nouvelle page personnelle :'''||'''Groupes utilisés pour "
+"les listes de contrôle d'accès :'''||\n"
 "||<<NewPage(HomepageReadWritePageTemplate,page ouverte en écriture,%"
 "(username)s)>>||[\"%(username)s/GroupeDesRédacteurs\"]||\n"
 "||<<NewPage(HomepageReadPageTemplate,page en lecture seule,%(username)s)>>||"
@@ -2350,7 +2391,7 @@
 msgstr "Gestion de MesPages"
 
 msgid "Rename all /subpages too?"
-msgstr "Renommer également tous les /SousPages ?"
+msgstr "Renommer également tous les /SousPages ?"
 
 msgid "New name"
 msgstr "Nouveau nom"
@@ -2359,42 +2400,41 @@
 msgstr "Raison de ce changement de nom (optionnelle)"
 
 msgid "Really rename this page?"
-msgstr "Voulez-vous vraiment renommer cette page ?"
+msgstr "Voulez-vous vraiment renommer cette page ?"
 
 msgid "Your subscription to this page has been removed."
 msgstr "Votre abonnement à cette page a été supprimé."
 
 msgid "Can't remove regular expression subscription!"
 msgstr ""
-"Impossible de retirer un abonnement défini par une expression rationnelle !"
-
-#, fuzzy
+"Impossible de retirer un abonnement défini par une expression rationnelle !"
+
 msgid "Edit the subscription regular expressions in your settings."
 msgstr ""
-"Modifiez les expressions rationnelles contenues dans vos "
-"PréférencesUtilisateur."
+"Modifiez les expressions rationnelles d'abonnement contenues dans vos "
+"préférences."
 
 msgid "You need to be subscribed to unsubscribe."
 msgstr "Il est nécessaire d'être abonné pour se désabonner."
 
 msgid "TextCha: Wrong answer! Go back and try again..."
-msgstr "TextCha : mauvaise réponse ! Revenez en arrière et réessayez..."
+msgstr "TextCha : mauvaise réponse ! Revenez en arrière et réessayez..."
 
 msgid "Copy all /subpages too?"
-msgstr "Copier également toutes les /SousPages ?"
+msgstr "Copier également toutes les /SousPages ?"
 
 msgid "Optional reason for the copying"
 msgstr "Motif de cette copie (optionnel)"
 
 msgid "Really copy this page?"
-msgstr "Voulez-vous vraiment copier cette page ?"
+msgstr "Voulez-vous vraiment copier cette page ?"
 
 msgid "No older revisions available!"
 msgstr "Il n'y a pas de version antérieure."
 
 #, python-format
 msgid "Diff for \"%s\""
-msgstr "Modifications de « %s »"
+msgstr "Modifications de « %s »"
 
 #, python-format
 msgid "Differences between revisions %d and %d"
@@ -2404,17 +2444,15 @@
 msgid "(spanning %d versions)"
 msgstr "(s'étendant sur %d versions)"
 
-#, fuzzy
 msgid "Previous change"
-msgstr "Précédent"
-
-#, fuzzy
+msgstr "Modification précédente"
+
 msgid "Next change"
-msgstr "Aucune modification"
+msgstr "Modification suivante"
 
 #, python-format
 msgid "The page was saved %(count)d times, though!"
-msgstr "Cette page a pourtant été enregistrée %(count)d fois !"
+msgstr "Cette page a pourtant été enregistrée %(count)d fois !"
 
 msgid "(ignoring whitespace)"
 msgstr "(en ignorant les blancs)"
@@ -2425,48 +2463,48 @@
 msgid "Load"
 msgstr "Charger"
 
-#, fuzzy
 msgid "Pagename not specified!"
-msgstr "Le nom du fichier à ajouter n'est pas spécifié !"
+msgstr "Nom de page non indiqué !"
 
 msgid "Upload page content"
-msgstr ""
+msgstr "Envoyer le contenu de la page"
 
 msgid ""
 "You can upload content for the page named below. If you change the page "
 "name, you can also upload content for another page. If the page name is "
 "empty, we derive the page name from the file name."
 msgstr ""
+"Vous pouvez ajouter du contenu à la page nommé ci-dessous. Si vous changez "
+"le nom de la page, vous pourrez ainsi ajouter du contenu à une autre page. "
+"Si le champ donnant le nom de la page est vide, le nom du fichier sera "
+"utilisé comme nom de page."
 
 msgid "File to load page content from"
-msgstr ""
+msgstr "Choisissez le fichier d'où importer le contenu de la page"
 
 msgid "Comment"
 msgstr "Commentaire"
 
-#, fuzzy
 msgid "Page Name"
-msgstr "Nom du paquet"
+msgstr "Nom de la page"
 
 msgid "You are not allowed to subscribe to a page you can't read."
 msgstr ""
 "Vous n'êtes pas autorisé à vous abonner à une page que vous ne pouvez pas "
 "lire."
 
-#, fuzzy
 msgid "This wiki is not enabled for mail/Jabber processing."
-msgstr "La gestion du courrier n'est pas activée sur ce wiki."
+msgstr "La traitement du courrier et de Jabber n'est pas activée sur ce wiki."
 
 msgid "You must log in to use subscriptions."
 msgstr "Vous devez être identifié pour utiliser l'abonnement."
 
-#, fuzzy
 msgid ""
 "Add your email address or Jabber ID in your user settings to use "
 "subscriptions."
 msgstr ""
-"Ajoutez votre adresse électronique dans les PréférencesUtilisateur pour "
-"utiliser l'abonnement."
+"Ajoutez votre adresse électronique ou votre identifiant Jabber dans vos "
+"préférences pour utiliser l'abonnement."
 
 msgid "You are already subscribed to this page."
 msgstr "Vous êtes déjà abonné à cette page."
@@ -2482,16 +2520,16 @@
 
 #, python-format
 msgid "Page size: %d"
-msgstr "Taille de la page : %d"
+msgstr "Taille de la page : %d"
 
 msgid "SHA digest of this page's content is:"
-msgstr "L'empreinte SHA du contenu de cette page est :"
+msgstr "L'empreinte SHA du contenu de cette page est :"
 
 msgid "The following users subscribed to this page:"
-msgstr "Les utilisateurs suivants sont abonnés à cette page :"
+msgstr "Les utilisateurs suivants sont abonnés à cette page :"
 
 msgid "This page links to the following pages:"
-msgstr "Cette page est reliée aux pages suivantes :"
+msgstr "Cette page est reliée aux pages suivantes :"
 
 msgid "Size"
 msgstr "Taille"
@@ -2505,9 +2543,8 @@
 msgid "view"
 msgstr "voir"
 
-#, fuzzy
 msgid "to previous"
-msgstr "Précédent"
+msgstr "précédent"
 
 msgid "get"
 msgstr "obtenir"
@@ -2519,15 +2556,15 @@
 msgstr "Historique des révisions"
 
 msgid "No log entries found."
-msgstr "Aucune entrée de journal trouvée !"
+msgstr "Aucune entrée de journal trouvée !"
 
 #, python-format
 msgid "Info for \"%s\""
-msgstr "Informations sur « %s »"
+msgstr "Informations sur « %s »"
 
 #, python-format
 msgid "Show \"%(title)s\""
-msgstr "Voir : %(title)s"
+msgstr "Voir : %(title)s"
 
 msgid "General Page Infos"
 msgstr "Informations générales"
@@ -2541,41 +2578,40 @@
 msgstr "Supprimer"
 
 msgid "Delete all /subpages too?"
-msgstr "Supprimer également toutes les /SousPages ?"
+msgstr "Supprimer également toutes les /SousPages ?"
 
 msgid "Optional reason for the deletion"
 msgstr "Raison de cette suppression (optionnelle)"
 
 msgid "Really delete this page?"
-msgstr "Voulez-vous vraiment supprimer cette page ?"
+msgstr "Voulez-vous vraiment supprimer cette page ?"
 
 msgid "You are not allowed to create the supplementation page."
 msgstr "Vous n'êtes pas autorisé à créer une page complémentaire"
 
-#, fuzzy
 msgid "Only superuser is allowed to use this action."
-msgstr "Vous n'êtes pas autorisé à utiliser cette action."
+msgstr "Seul le superutilisateur peut réaliser cette action."
 
 #, python-format
 msgid "Subscribe users to the page %s"
 msgstr "Abonner les utilisateurs à la page %s"
 
 msgid "Enter user names (comma separated):"
-msgstr "Entrez les noms d'utilisateurs (séparés par des virgules) :"
+msgstr "Entrez les noms d'utilisateurs (séparés par des virgules) :"
 
 #, python-format
 msgid "Subscribed for %s:"
 msgstr "Abonné à %s"
 
 msgid "Not a user:"
-msgstr "N'est pas un utilisateur :"
+msgstr "N'est pas un utilisateur :"
 
 msgid "You are not allowed to perform this action."
 msgstr "Vous n'êtes pas autorisé à réaliser cette action."
 
 #, python-format
 msgid "(!) Only pages changed since '''%s''' are being displayed!"
-msgstr "(!) Seules les pages modifiées depuis '''%s''' sont affichées !"
+msgstr "(!) Seules les pages modifiées depuis '''%s''' sont affichées !"
 
 msgid ""
 "/!\\ The modification date you entered was not recognized and is therefore "
@@ -2583,34 +2619,26 @@
 msgstr ""
 "/!\\ La date de modification que vous avez indiqué n'a pas été reconnue. En "
 "conséquence, elle ne sera pas prise en compte pour les résultats de la "
-"recherche !"
+"recherche !"
 
 #, python-format
 msgid "Title Search: \"%s\""
-msgstr "Recherche de « %s » dans les titres"
+msgstr "Recherche de « %s » dans les titres"
 
 #, python-format
 msgid "Advanced Search: \"%s\""
-msgstr "Recherche avancée : « %s »"
+msgstr "Recherche avancée : « %s »"
 
 #, python-format
 msgid "Full Text Search: \"%s\""
-msgstr "Recherche de « %s » dans les textes"
-
-#, python-format
-msgid ""
-"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching "
-"for more information."
-msgstr ""
-"Le texte de cette recherche, « {{{%s}}} », est incorrect. Veuillez consulter "
-"la page AideDeLaRecherche pour plus d'informations."
+msgstr "Recherche de « %s » dans les textes"
 
 #, python-format
 msgid ""
 "Your search query {{{\"%s\"}}} didn't return any results. Please change some "
 "terms and refer to HelpOnSearching for more information.%s"
 msgstr ""
-"Votre recherche « {{{%s}}} » n'a donné aucun résultat. Veuillez la modifier "
+"Votre recherche « {{{%s}}} » n'a donné aucun résultat. Veuillez la modifier "
 "ou consulter la page AideDeLaRecherche pour plus d'informations. %s"
 
 msgid "(!) Consider performing a"
@@ -2628,11 +2656,11 @@
 
 msgid "Click here to perform a full-text search with your search terms!"
 msgstr ""
-"Cliquez ici pour appliquer cette recherche au texte des pages du wiki !"
+"Cliquez ici pour appliquer cette recherche au texte des pages du wiki !"
 
 msgid "User account created! You can use this account to login now..."
 msgstr ""
-"Compte utilisateur créé ! Vous pouvez l'utiliser dès à présent pour vous "
+"Compte utilisateur créé ! Vous pouvez l'utiliser dès à présent pour vous "
 "connecter..."
 
 msgid "TextCha (required)"
@@ -2642,7 +2670,7 @@
 msgstr "Créer le compte"
 
 msgid "Create Account"
-msgstr ""
+msgstr "Créer un Compte"
 
 msgid "You must login to add a quicklink."
 msgstr "Vous devez être identifié pour ajouter un lien rapide."
@@ -2654,7 +2682,7 @@
 msgstr "Il n'a pas été possible d'ajouter pour vous un lien vers cette page."
 
 msgid "You already have a quicklink to this page."
-msgstr "Vous avez déjà créé un lien rapide vers cette page !"
+msgstr "Vous avez déjà créé un lien rapide vers cette page !"
 
 msgid "You are not allowed to use this action."
 msgstr "Vous n'êtes pas autorisé à utiliser cette action."
@@ -2669,13 +2697,13 @@
 "(totalwords)d words%(localwords)s and are highlighted below:"
 msgstr ""
 "Les %(badwords)d mots suivants n'ont pu être trouvé dans le dictionnaire sur "
-"un total de %(totalwords)d mots%(localwords)s et sont surlignés ci-dessous :"
+"un total de %(totalwords)d mots%(localwords)s et sont surlignés ci-dessous :"
 
 msgid "Add checked words to dictionary"
 msgstr "Ajouter les mots cochés au dictionnaire"
 
 msgid "No spelling errors found!"
-msgstr "Aucune faute d'orthographe n'a été trouvée !"
+msgstr "Aucune faute d'orthographe n'a été trouvée !"
 
 msgid "You can't save spelling words."
 msgstr "Impossible de sauvegarder le nouveau vocabulaire."
@@ -2688,9 +2716,8 @@
 msgid "You are now logged out."
 msgstr "Vous êtes maintenant déconnecté."
 
-#, fuzzy
 msgid "If this account exists an email was sent."
-msgstr "Si le compte existe, un courrier va être envoyé."
+msgstr "Si ce compte existe, un courrier a été envoyé."
 
 msgid ""
 "This wiki is not enabled for mail processing.\n"
@@ -2699,51 +2726,45 @@
 "La gestion du courrier n'est pas activée sur ce wiki.\n"
 "Contactez le propriétaire de ce wiki, qui pourra l'activer."
 
-#, fuzzy
 msgid "Please provide a valid email address or a username!"
-msgstr "Veuillez fournir une adresse électronique valide !"
+msgstr ""
+"Veuillez indiquer une adresse électronique ou un nom d'utilisateur correct !"
 
 msgid "Mail me my account data"
 msgstr "Envoyez-moi mes identifiants de connexion"
 
-#, fuzzy
 msgid "Recovery token"
-msgstr "Tout restaurer !"
-
-#, fuzzy
+msgstr "Code de récupération"
+
 msgid "New password"
-msgstr "Mot de passe"
-
-#, fuzzy
+msgstr "Nouveau mot de passe"
+
 msgid "New password (repeat)"
-msgstr "Répétez le mot de passe"
-
-#, fuzzy
+msgstr "Nouveau mot de passe (vérification)"
+
 msgid "Reset my password"
-msgstr "Mot de passe"
+msgstr "Réinitialiser mon mot de passe"
 
 msgid "Your password has been changed, you can log in now."
-msgstr ""
+msgstr "Votre mot de passe a été changé, vous pouvez vous connecter."
 
 msgid "Your token is invalid!"
-msgstr ""
-
-#, fuzzy
+msgstr "Le code indiqué est incorrect !"
+
 msgid "Password reset"
-msgstr "Répétez le mot de passe"
-
-#, fuzzy
+msgstr "Réinitialisation du mot de passe"
+
 msgid ""
 "\n"
 "== Password reset ==\n"
 "Enter a new password below."
 msgstr ""
-"Mot de passe manquant. Veuillez indiquer un nom d'utilisateur et un mot de "
-"passe."
-
-#, fuzzy
+"\n"
+"== Réinitialisation du mot de passe ==\n"
+"Veuillez indiquer le nouveau mot de passe ci-dessous."
+
 msgid "Lost password"
-msgstr "Mot de passe"
+msgstr "Mot de passe oublié"
 
 msgid ""
 "\n"
@@ -2754,6 +2775,14 @@
 "used to change your password. The email will also contain further\n"
 "instructions."
 msgstr ""
+"\n"
+"== Récupération d'un mot de passe oublié ==\n"
+"Si vous avez oublié votre mot de passe, donnez votre adresse de courrier \n"
+"électronique et cliquez sur '''Envoyez-moi mes identifiants de "
+"connexion'''.\n"
+"Vous recevrez un courrier contenant un mot-clef de récupération qui peut\n"
+"être utilisé pour changer votre mot de passe. Le courrier contiendra\n"
+"d'autres instructions."
 
 msgid ""
 "\n"
@@ -2761,10 +2790,15 @@
 "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 ""
+"\n"
+"=== Réinitialisation du mot de passe ===\n"
+"Si vous avez déjà reçu le courrier électronique avec le mot-clef\n"
+"de récupération, entrez votre nom d'utilisateur, le mot-clef et\n"
+"un nouveau mot de passe (deux fois) ci-dessous"
 
 #, python-format
 msgid "Local Site Map for \"%s\""
-msgstr "Carte locale du site pour « %s »"
+msgstr "Carte locale du site pour « %s »"
 
 msgid "Pages"
 msgstr "Pages"
@@ -2773,7 +2807,7 @@
 msgstr "Sélection de l'auteur"
 
 msgid "Revert all!"
-msgstr "Tout restaurer !"
+msgstr "Tout restaurer !"
 
 #, python-format
 msgid "[%d attachments]"
@@ -2786,11 +2820,11 @@
 msgstr "Il y a <a href=\"%(link)s\">%(count)s pièces jointes</a> à cette page."
 
 msgid "Filename of attachment not specified!"
-msgstr "Le nom du fichier à ajouter n'est pas spécifié !"
+msgstr "Le nom du fichier à ajouter n'est pas spécifié !"
 
 #, python-format
 msgid "Attachment '%(filename)s' does not exist!"
-msgstr "La pièce jointe « %(filename)s » n'existe pas !"
+msgstr "La pièce jointe « %(filename)s » n'existe pas !"
 
 msgid ""
 "To refer to attachments on a page, use '''{{{attachment:filename}}}''', \n"
@@ -2843,15 +2877,14 @@
 
 #, python-format
 msgid "Unsupported AttachFile sub-action: %s"
-msgstr "Sous-action d'ajout non disponible : %s"
+msgstr "Sous-action d'ajout non disponible : %s"
 
 #, python-format
 msgid "Attachments for \"%(pagename)s\""
-msgstr "Pièces jointes de « %(pagename)s »"
-
-#, fuzzy
+msgstr "Pièces jointes de « %(pagename)s »"
+
 msgid "You are not allowed to overwrite a file attachment of this page."
-msgstr "Vous n'êtes pas autorisé à voir les pièces jointes de cette page."
+msgstr "Vous n'êtes pas autorisé à remplacer les pièces jointes de cette page."
 
 msgid ""
 "No file content. Delete non ASCII characters from the file name and try "
@@ -2865,7 +2898,7 @@
 "Attachment '%(target)s' (remote name '%(filename)s') with %(bytes)d bytes "
 "saved."
 msgstr ""
-"Pièce jointe « %(target)s » enregistrée (nom distant « %(filename)s » ; %"
+"Pièce jointe « %(target)s » enregistrée (nom distant « %(filename)s » ; %"
 "(bytes)d octets)."
 
 msgid "You are not allowed to save a drawing on this page."
@@ -2874,48 +2907,48 @@
 msgid "You are not allowed to delete attachments on this page."
 msgstr "Vous n'êtes pas autorisé à supprimer les pièces jointes de cette page."
 
-#, fuzzy, python-format
+#, python-format
 msgid "Attachment '%(new_pagename)s/%(new_filename)s' already exists."
-msgstr "La pièce jointe « %(filename)s » existe déjà."
-
-#, fuzzy, python-format
+msgstr "La pièce jointe « %(new_pagename)s/%(new_filename)s » existe déjà."
+
+#, python-format
 msgid ""
 "Attachment '%(pagename)s/%(filename)s' moved to '%(new_pagename)s/%"
 "(new_filename)s'."
-msgstr "La pièce jointe « %(filename)s » a été déplacée vers %(page)s."
+msgstr ""
+"La pièce jointe « %(pagename)s/%(filename)s » a été déplacée vers « %"
+"(new_pagename)s/%(new_filename)s »."
 
 msgid "Nothing changed"
 msgstr "Aucune modification"
 
-#, fuzzy, python-format
+#, python-format
 msgid "Page '%(new_pagename)s' does not exist or you don't have enough rights."
 msgstr ""
-"La page %(newpagename)s n'existe pas ou bien vous ne disposez pas des droits "
-"nécessaires."
+"La page %(new_pagename)s n'existe pas ou bien vous ne disposez pas des "
+"droits nécessaires."
 
 msgid "Move aborted!"
-msgstr "Déplacement annulé !"
+msgstr "Déplacement annulé !"
 
 msgid "Please use the interactive user interface to move attachments!"
-msgstr "Utilisez l'interface interactive pour déplacer des pièces jointes !"
+msgstr "Utilisez l'interface interactive pour déplacer des pièces jointes !"
 
 msgid "You are not allowed to move attachments from this page."
 msgstr ""
 "Vous n'êtes pas autorisé à déplacer des pièces jointes depuis cette page."
 
-#, fuzzy
 msgid "Move aborted because new page name is empty."
-msgstr "Impossible de renommer sans indiquer un nouveau nom"
+msgstr "Impossible de renommer sans indiquer un nouveau nom."
 
 #, python-format
 msgid "Please use a valid filename for attachment '%(filename)s'."
 msgstr ""
-"Veuillez indiquer un nom de fichier valable pour la pièce jointe « %(filename)"
-"s »."
-
-#, fuzzy
+"Veuillez indiquer un nom de fichier valable pour la pièce jointe « %(filename)"
+"s »."
+
 msgid "Move aborted because new attachment name is empty."
-msgstr "Déplacement annulé : le nom de la pièce jointe est vide"
+msgstr "Déplacement annulé : le nom de la nouvelle pièce jointe est vide."
 
 msgid "Move"
 msgstr "Déplacer"
@@ -2934,54 +2967,65 @@
 
 #, python-format
 msgid "Attachment '%(filename)s' installed."
-msgstr "Pièce jointe « %(filename)s » installée."
+msgstr "Pièce jointe « %(filename)s » installée."
 
 msgid "You are not allowed to unzip attachments of this page."
 msgstr ""
 "Vous n'êtes pas autorisé à décompresser les pièces jointes de cette page."
 
 #, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too large (%(space)d kB missing)."
-msgstr ""
-"La pièce jointe « %(filename)s » n'a pu être décompressée, car les fichiers "
-"résultants seraient trop volumineux (il manque %(space)d ko)."
-
-#, python-format
-msgid ""
-"Attachment '%(filename)s' could not be unzipped because the resulting files "
-"would be too many (%(count)d missing)."
-msgstr ""
-"La pièce jointe « %(filename)s » n'a pu être décompressée, car les fichiers "
-"résultants seraient trop nombreux (%(count)d en trop)."
-
-#, python-format