changeset 5979:810aee12a186

merged
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 24 Mar 2013 14:58:56 +0100
parents f662e5f7ca82 (diff) 81b8ea1e9c6f (current diff)
children 6489ec33874d
files
diffstat 148 files changed, 20969 insertions(+), 1323 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Sat Sep 22 22:48:40 2012 +0200
+++ b/.hgtags	Sun Mar 24 14:58:56 2013 +0100
@@ -49,3 +49,6 @@
 ced05deb11ae935c2da7b315ae8aa8eb498a92a5 1.9.2
 b853ed5b996502bbf055c5a0d44da0a35a07cc5d 1.9.3
 56eaf32027f4dd097b8274e497a39af77c009712 1.9.4
+96244d7ca949da039370da53b82a41aa77f34637 1.9.5
+f2fb4b3ed8e506b86943c7c358fc78e5f5e78163 1.9.6
+ea6910fbac3dbbdca7d06c9e240e17f066880782 1.9.7
--- a/Makefile	Sat Sep 22 22:48:40 2012 +0200
+++ b/Makefile	Sun Mar 24 14:58:56 2013 +0100
@@ -15,9 +15,9 @@
 	-mkdir build
 	wget -U MoinMoin/Makefile -O build/INSTALL.html "http://master19.moinmo.in/InstallDocs?action=print"
 	sed \
-		-e 's|http://master19.moinmo.in/moin_static...|../MoinMoin/web/static/htdocs|g' \
-		-e 's|http://static.moinmo.in/moin_static...|../MoinMoin/web/static/htdocs|g' \
-		-e 's|/moin_static...|../MoinMoin/web/static/htdocs|g' \
+		-e 's|http://master19.moinmo.in/moin_static..|../MoinMoin/web/static/htdocs|g' \
+		-e 's|http://static.moinmo.in/moin_static..|../MoinMoin/web/static/htdocs|g' \
+		-e 's|/moin_static..|../MoinMoin/web/static/htdocs|g' \
 		-e 's|/InstallDocs#|#|g' \
 		-e 's|/InstallDocs/QuickInstall#qdlinux|#InstallDocs.2BAC8-QuickInstall.2BAC8-Linux.Linux:_Detailed_Quick_Installation|g' \
 		-e 's|/InstallDocs/QuickInstall#qdmac|#InstallDocs.2BAC8-QuickInstall.2BAC8-MacOSX.Mac:_Detailed_Quick_Installation|g' \
@@ -27,9 +27,9 @@
 
 	wget -U MoinMoin/Makefile -O build/UPDATE.html "http://master19.moinmo.in/HelpOnUpdating?action=print"
 	sed \
-		-e 's|http://master19.moinmo.in/moin_static...|../MoinMoin/web/static/htdocs|g' \
-		-e 's|http://static.moinmo.in/moin_static...|../MoinMoin/web/static/htdocs|g' \
-		-e 's|/moin_static...|../MoinMoin/web/static/htdocs|g' \
+		-e 's|http://master19.moinmo.in/moin_static..|../MoinMoin/web/static/htdocs|g' \
+		-e 's|http://static.moinmo.in/moin_static..|../MoinMoin/web/static/htdocs|g' \
+		-e 's|/moin_static..|../MoinMoin/web/static/htdocs|g' \
 		-e 's|href=./MoinMoin|href=\"http://master19.moinmo.in/MoinMoin|g' \
 		-e 's|href=./EditedSystemPages|href=\"http://master19.moinmo.in/EditedSystemPages|g' \
         build/UPDATE.html >docs/UPDATE.html
@@ -51,8 +51,8 @@
 # Should be used only on TW machine
 underlay:
 	rm -rf $(share)/underlay
-	MoinMoin/script/moin.py --config-dir=/srv/moin/cfg/1.9 --wiki-url=http://master19.moinmo.in/ maint globaledit
-	MoinMoin/script/moin.py --config-dir=/srv/moin/cfg/1.9 --wiki-url=http://master19.moinmo.in/ maint reducewiki --target-dir=$(share)/underlay
+	MoinMoin/script/moin.py --config-dir=/srv/cfg/1.9 --wiki-url=http://master19.moinmo.in/ maint globaledit
+	MoinMoin/script/moin.py --config-dir=/srv/cfg/1.9 --wiki-url=http://master19.moinmo.in/ maint reducewiki --target-dir=$(share)/underlay
 	rm -rf $(share)/underlay/pages/InterWikiMap
 	rm -rf $(share)/underlay/pages/MoinPagesEditorGroup
 	cd $(share); rm -f underlay.tar; tar cf underlay.tar underlay
@@ -62,15 +62,20 @@
 	@MoinMoin/script/moin.py --config-dir=MoinMoin/_tests --wiki-url=http://localhost/ maint mkpagepacks
 	cd $(share) ; rm -rf underlay
 	cp -a $(testwiki)/underlay $(share)/
-	
-dist:
+
+dist-clean:
 	-rm MANIFEST
 	-rm -rf tests/wiki
 	-rm -rf wiki/data/cache/{__metalock__,__session__,wikiconfig}
 	->wiki/data/event-log
 	->wiki/data/edit-log
+
+dist: dist-clean
 	python setup.py sdist
 
+dist-release: dist-clean
+	python setup.py register sdist upload --identity="Thomas Waldmann" --sign
+
 # Create patchlevel module
 patchlevel:
 	@echo -e patchlevel = "\"`hg identify`\"\n" >MoinMoin/patchlevel.py
--- a/MoinMoin/PageEditor.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/PageEditor.py	Sun Mar 24 14:58:56 2013 +0100
@@ -12,7 +12,7 @@
 
     @copyright: 2000-2004 by Juergen Hermann <jh@web.de>,
                 2005-2007 by MoinMoin:ThomasWaldmann,
-                2007 by MoinMoin:ReimarBauer
+                2007-2013 by MoinMoin:ReimarBauer
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -88,6 +88,8 @@
         @keyword do_revision_backup: if 0, suppress making a page backup per revision
         @keyword do_editor_backup: if 0, suppress saving of draft copies
         @keyword uid_override: override user id and name (default None)
+        @keyword mtime: time for edit-log and event-log (using current time in UTC, if not given)
+                        number of seconds since the epoch, see the time module
         """
         Page.__init__(self, request, page_name, **keywords)
         self._ = request.getText
@@ -95,6 +97,7 @@
         self.do_revision_backup = keywords.get('do_revision_backup', 1)
         self.do_editor_backup = keywords.get('do_editor_backup', 1)
         self.uid_override = keywords.get('uid_override', None)
+        self.mtime = keywords.get('mtime')
 
         self.lock = PageLock(self)
 
@@ -996,6 +999,8 @@
             else:
                 filesys.rename(cltfn, clfn)
 
+            if self.mtime is not None:
+                mtime_usecs = self.mtime
             if not deleted:
                 # save to page file
                 pagefile = os.path.join(revdir, revstr)
@@ -1003,14 +1008,16 @@
                 # Write the file using text/* mime type
                 f.write(self.encodeTextMimeType(text))
                 f.close()
-                mtime_usecs = wikiutil.timestamp2version(os.path.getmtime(pagefile))
+                if self.mtime is None:
+                    mtime_usecs = os.path.getmtime(pagefile)
                 # set in-memory content
                 self.set_raw_body(text)
             else:
-                mtime_usecs = wikiutil.timestamp2version(time.time())
+                if self.mtime is None:
+                    mtime_usecs = time.time()
                 # set in-memory content
                 self.set_raw_body(None)
-
+            mtime_usecs = wikiutil.timestamp2version(mtime_usecs)
             # reset page object
             self.reset()
 
--- a/MoinMoin/__init__.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/__init__.py	Sun Mar 24 14:58:56 2013 +0100
@@ -3,7 +3,7 @@
 MoinMoin - a wiki engine in Python
 
 @copyright: 2000-2006 by Juergen Hermann <jh@web.de>,
-            2002-2009 MoinMoin:ThomasWaldmann
+            2002-2013 MoinMoin:ThomasWaldmann
 @license: GNU GPL, see COPYING for details.
 """
 
--- a/MoinMoin/_tests/test_user.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/_tests/test_user.py	Sun Mar 24 14:58:56 2013 +0100
@@ -4,6 +4,7 @@
 
     @copyright: 2003-2004 by Juergen Hermann <jh@web.de>
                 2009 by ReimarBauer
+                2013 by MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -19,18 +20,27 @@
     def testAscii(self):
         """user: encode ascii password"""
         # u'MoinMoin' and 'MoinMoin' should be encoded to same result
-        expected = "{SSHA}xkDIIx1I7A4gC98Vt/+UelIkTDYxMjM0NQ=="
-
-        result = user.encodePassword("MoinMoin", salt='12345')
-        assert result == expected
-        result = user.encodePassword(u"MoinMoin", salt='12345')
-        assert result == expected
+        cfg = self.request.cfg
+        tests = [
+            ('{PASSLIB}', '12345', "{PASSLIB}$6$rounds=1001$12345$jrPUCzPJt1yiixDbzIgSBoKED0/DlNDTHZN3lVarCtN6IM/.LoAw5pgUQH112CErU6wS8HXTZNpqb7wVjHLs/0"),
+            ('{SSHA}', '12345', "{SSHA}xkDIIx1I7A4gC98Vt/+UelIkTDYxMjM0NQ=="),
+        ]
+        for scheme, salt, expected in tests:
+            result = user.encodePassword(cfg, "MoinMoin", salt=salt, scheme=scheme)
+            assert result == expected
+            result = user.encodePassword(cfg, u"MoinMoin", salt=salt, scheme=scheme)
+            assert result == expected
 
     def testUnicode(self):
         """ user: encode unicode password """
-        result = user.encodePassword(u'סיסמה סודית בהחלט', salt='12345') # Hebrew
-        expected = "{SSHA}YiwfeVWdVW9luqyVn8t2JivlzmUxMjM0NQ=="
-        assert result == expected
+        cfg = self.request.cfg
+        tests = [
+            ('{PASSLIB}', '12345', "{PASSLIB}$6$rounds=1001$12345$5srFB66ZCu2JgGwPgdfb1lHRmqkjnKC/RxdsFlWn2WzoQh3btIjH6Ai1LJV9iYLDa9kLP/VQYa4DHLkRnaBw8."),
+            ('{SSHA}', '12345', "{SSHA}YiwfeVWdVW9luqyVn8t2JivlzmUxMjM0NQ=="),
+            ]
+        for scheme, salt, expected in tests:
+            result = user.encodePassword(cfg, u'סיסמה סודית בהחלט', salt=salt, scheme=scheme) # Hebrew
+            assert result == expected
 
 
 class TestLoginWithPassword(object):
@@ -46,6 +56,8 @@
         self.request.user = user.User(self.request)
 
         self.user = None
+        self.passlib_support = self.request.cfg.passlib_support
+        self.password_scheme = self.request.cfg.password_scheme
 
     def teardown_method(self, method):
         """ Run after each test
@@ -97,48 +109,118 @@
     def test_auth_with_apr1_stored_password(self):
         """
         Create user with {APR1} password and check that user can login.
+        Also check if auto-upgrade happens and is saved to disk.
         """
         # Create test user
         name = u'Test User'
+        password = '12345'
         # generated with "htpasswd -nbm blaze 12345"
-        password = '{APR1}$apr1$NG3VoiU5$PSpHT6tV0ZMKkSZ71E3qg.' # 12345
-        self.createUser(name, password, True)
+        pw_hash = '{APR1}$apr1$NG3VoiU5$PSpHT6tV0ZMKkSZ71E3qg.'
+        self.createUser(name, pw_hash, True)
 
         # Try to "login"
-        theuser = user.User(self.request, name=name, password='12345')
+        theuser = user.User(self.request, name=name, password=password)
         assert theuser.valid
+        # Check if the stored password was auto-upgraded on login and saved
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.enc_password.startswith(self.password_scheme)
 
     def test_auth_with_md5_stored_password(self):
         """
         Create user with {MD5} password and check that user can login.
+        Also check if auto-upgrade happens and is saved to disk.
         """
         # Create test user
         name = u'Test User'
-        password = '{MD5}$1$salt$etVYf53ma13QCiRbQOuRk/' # 12345
-        self.createUser(name, password, True)
+        password = '12345'
+        pw_hash = '{MD5}$1$salt$etVYf53ma13QCiRbQOuRk/'
+        self.createUser(name, pw_hash, True)
 
         # Try to "login"
-        theuser = user.User(self.request, name=name, password='12345')
+        theuser = user.User(self.request, name=name, password=password)
         assert theuser.valid
+        # Check if the stored password was auto-upgraded on login and saved
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.enc_password.startswith(self.password_scheme)
 
     def test_auth_with_des_stored_password(self):
         """
         Create user with {DES} password and check that user can login.
+        Also check if auto-upgrade happens and is saved to disk.
         """
         # Create test user
         name = u'Test User'
+        password = '12345'
         # generated with "htpasswd -nbd blaze 12345"
-        password = '{DES}gArsfn7O5Yqfo' # 12345
-        self.createUser(name, password, True)
+        pw_hash = '{DES}gArsfn7O5Yqfo'
+        self.createUser(name, pw_hash, True)
 
         try:
             import crypt
             # Try to "login"
-            theuser = user.User(self.request, name=name, password='12345')
+            theuser = user.User(self.request, name=name, password=password)
             assert theuser.valid
+            # Check if the stored password was auto-upgraded on login and saved
+            theuser = user.User(self.request, name=name, password=password)
+            assert theuser.enc_password.startswith(self.password_scheme)
         except ImportError:
             py.test.skip("Platform does not provide crypt module!")
 
+    def test_auth_with_sha_stored_password(self):
+        """
+        Create user with {SHA} password and check that user can login.
+        Also check if auto-upgrade happens and is saved to disk.
+        """
+        # Create test user
+        name = u'Test User'
+        password = '12345'
+        pw_hash = '{SHA}jLIjfQZ5yojbZGTqxg2pY0VROWQ='
+        self.createUser(name, pw_hash, True)
+
+        # Try to "login"
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.valid
+        # Check if the stored password was auto-upgraded on login and saved
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.enc_password.startswith(self.password_scheme)
+
+    def test_auth_with_ssha_stored_password(self):
+        """
+        Create user with {SSHA} password and check that user can login.
+        Also check if auto-upgrade happens and is saved to disk.
+        """
+        # Create test user
+        name = u'Test User'
+        password = '12345'
+        pw_hash = '{SSHA}dbeFtH5EGkOI1jgPADlGZgHWq072TIsKqWfHX7zZbUQa85Ze8774Rg=='
+        self.createUser(name, pw_hash, True)
+
+        # Try to "login"
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.valid
+        # Check if the stored password was auto-upgraded on login and saved
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.enc_password.startswith(self.password_scheme)
+
+    def test_auth_with_passlib_stored_password(self):
+        """
+        Create user with {PASSLIB} password and check that user can login.
+        """
+        if not self.passlib_support:
+            py.test.skip("test requires passlib, but passlib_support is False")
+        # Create test user
+        name = u'Test User'
+        password = '12345'
+        pw_hash = '{PASSLIB}$6$rounds=1001$/AVWSh/RUWpcppfl$8DCRGLaBD3KoV4Ag67sUv6b2QdrUFXk1yWCxqWnBLJ.iHSe4Piv6nqzSQgELeLPIvwTC9APaWv1XCTOHjkLOj/'
+        self.createUser(name, pw_hash, True)
+
+        # Try to "login"
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.valid
+        # Check if the stored password was auto-upgraded on login and saved
+        theuser = user.User(self.request, name=name, password=password)
+        assert theuser.enc_password.startswith(self.password_scheme)
+
     def testSubscriptionSubscribedPage(self):
         """ user: tests isSubscribedTo  """
         pagename = u'HelpMiscellaneous'
@@ -179,63 +261,6 @@
 
         assert not theUser.exists()
 
-    def test_upgrade_password_from_sha_to_ssha(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)
-
-        # User is not required to be valid
-        theuser = user.User(self.request, name=name, password='12345')
-        assert theuser.enc_password[:6] == '{SSHA}'
-
-    def test_upgrade_password_from_apr1_to_ssha(self):
-        """
-        Create user with {APR1} password and check that logging in
-        upgrades to {SSHA}.
-        """
-        # Create test user
-        name = u'Test User'
-        # generated with "htpasswd -nbm blaze 12345"
-        password = '{APR1}$apr1$NG3VoiU5$PSpHT6tV0ZMKkSZ71E3qg.' # 12345
-        self.createUser(name, password, True)
-
-        # User is not required to be valid
-        theuser = user.User(self.request, name=name, password='12345')
-        assert theuser.enc_password[:6] == '{SSHA}'
-
-    def test_upgrade_password_from_md5_to_ssha(self):
-        """
-        Create user with {MD5} password and check that logging in
-        upgrades to {SSHA}.
-        """
-        # Create test user
-        name = u'Test User'
-        password = '{MD5}$1$salt$etVYf53ma13QCiRbQOuRk/' # 12345
-        self.createUser(name, password, True)
-
-        # User is not required to be valid
-        theuser = user.User(self.request, name=name, password='12345')
-        assert theuser.enc_password[:6] == '{SSHA}'
-
-    def test_upgrade_password_from_des_to_ssha(self):
-        """
-        Create user with {DES} password and check that logging in
-        upgrades to {SSHA}.
-        """
-        # Create test user
-        name = u'Test User'
-        # generated with "htpasswd -nbd blaze 12345"
-        password = '{DES}gArsfn7O5Yqfo' # 12345
-        self.createUser(name, password, True)
-
-        # User is not required to be valid
-        theuser = user.User(self.request, name=name, password='12345')
-        assert theuser.enc_password[:6] == '{SSHA}'
-
     def test_for_email_attribute_by_name(self):
         """
         checks for no access to the email attribute by getting the user object from name
@@ -269,7 +294,7 @@
         self.user.name = name
         self.user.email = email
         if not pwencoded:
-            password = user.encodePassword(password)
+            password = user.encodePassword(self.request.cfg, password)
         self.user.enc_password = password
 
         # Validate that we are not modifying existing user data file!
--- a/MoinMoin/_tests/wikiconfig.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/_tests/wikiconfig.py	Sun Mar 24 14:58:56 2013 +0100
@@ -31,3 +31,17 @@
     # used to check if it is really a wiki we may modify
     is_test_wiki = True
 
+    # for runnging tests without passlib support:
+    #passlib_support = False
+    #password_scheme = '{SSHA}'
+
+    # for running tests with passlib support:
+    passlib_crypt_context = dict(
+        schemes=["sha512_crypt", ],
+        # for the tests, we don't want to have varying rounds
+        sha512_crypt__vary_rounds=0,
+        # for the tests, we want to have a rather low rounds count,
+        # so the tests run quickly (do NOT use low counts in production!)
+        sha512_crypt__default_rounds=1001,
+    )
+
--- a/MoinMoin/action/AttachFile.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/action/AttachFile.py	Sun Mar 24 14:58:56 2013 +0100
@@ -22,7 +22,7 @@
                 2001-2004 by Juergen Hermann <jh@web.de>,
                 2005 MoinMoin:AlexanderSchremmer,
                 2005 DiegoOngaro at ETSZONE (diego@etszone.com),
-                2005-2007 MoinMoin:ReimarBauer,
+                2005-2013 MoinMoin:ReimarBauer,
                 2007-2008 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
@@ -310,7 +310,7 @@
     return (pagename, None, None)
 
 
-def _build_filelist(request, pagename, showheader, readonly, mime_type='*'):
+def _build_filelist(request, pagename, showheader, readonly, mime_type='*', filterfn=None):
     _ = request.getText
     fmt = request.html_formatter
 
@@ -320,6 +320,8 @@
 
     if mime_type != '*':
         files = [fname for fname in files if mime_type == mimetypes.guess_type(fname)[0]]
+    if filterfn is not None:
+        files = [fname for fname in files if filterfn(fname)]
 
     html = []
     if files:
@@ -394,7 +396,7 @@
                         links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='unzip')) +
                                      fmt.text(label_unzip) +
                                      fmt.url(0))
-            except RuntimeError:
+            except (RuntimeError, zipfile.BadZipfile, zipfile.LargeZipFile):
                 # 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
@@ -402,6 +404,8 @@
                 # RuntimeError is raised by zipfile stdlib module in case of
                 # problems (like inconsistent slash and backslash usage in the
                 # archive).
+                # BadZipfile/LargeZipFile are raised when there are some
+                # specific problems with the archive file.
                 logging.exception("An exception within zip file attachment handling occurred:")
 
             html.append(fmt.listitem(1))
@@ -601,6 +605,14 @@
     """ A storage container (multiple objects in 1 tarfile) """
 
     def __init__(self, request, pagename, containername):
+        """
+        @param pagename: a wiki page name
+        @param containername: the filename of the tar file.
+                              Make sure this is a simple filename, NOT containing any path components.
+                              Use wikiutil.taintfilename() to avoid somebody giving a container
+                              name that starts with e.g. ../../filename or you'll create a
+                              directory traversal and code execution vulnerability.
+        """
         self.request = request
         self.pagename = pagename
         self.containername = containername
@@ -668,6 +680,18 @@
 
 
 def move_file(request, pagename, new_pagename, attachment, new_attachment):
+    """
+    move a file attachment from pagename:attachment to new_pagename:new_attachment
+
+    @param pagename: original pagename
+    @param new_pagename: new pagename (may be same as original pagename)
+    @param attachment: original attachment filename
+                       note: attachment filename must not contain a path,
+                             use wikiutil.taintfilename() before calling move_file
+    @param new_attachment: new attachment filename (may be same as original filename)
+                       note: attachment filename must not contain a path,
+                             use wikiutil.taintfilename() before calling move_file
+    """
     _ = request.getText
 
     newpage = Page(request, new_pagename)
@@ -730,6 +754,10 @@
         upload_form(pagename, request, msg=_("Move aborted because new attachment name is empty."))
 
     attachment = request.form.get('oldattachmentname')
+    if attachment != wikiutil.taintfilename(attachment):
+        upload_form(pagename, request, msg=_("Please use a valid filename for attachment '%(filename)s'.") % {
+                              'filename': attachment})
+        return
     move_file(request, pagename, new_pagename, attachment, new_attachment)
 
 
@@ -989,7 +1017,7 @@
                         'filelist': ', '.join(not_overwritten), }
             else:
                 msg = _("Attachment '%(filename)s' unzipped.") % {'filename': filename}
-    except RuntimeError, err:
+    except (RuntimeError, zipfile.BadZipfile, zipfile.LargeZipFile), 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
@@ -997,6 +1025,8 @@
         # RuntimeError is raised by zipfile stdlib module in case of
         # problems (like inconsistent slash and backslash usage in the
         # archive).
+        # BadZipfile/LargeZipFile are raised when there are some
+        # specific problems with the archive file.
         logging.exception("An exception within zip file attachment handling occurred:")
         msg = _("A severe error occurred:") + ' ' + str(err)
 
@@ -1068,7 +1098,7 @@
                 request.write(wikiutil.escape("%-46s %s %12d\n" % (zinfo.filename, date, zinfo.file_size)))
             request.write("</pre>")
             return
-    except RuntimeError:
+    except (RuntimeError, zipfile.BadZipfile, zipfile.LargeZipFile):
         # 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
@@ -1076,6 +1106,8 @@
         # RuntimeError is raised by zipfile stdlib module in case of
         # problems (like inconsistent slash and backslash usage in the
         # archive).
+        # BadZipfile/LargeZipFile are raised when there are some
+        # specific problems with the archive file.
         logging.exception("An exception within zip file attachment handling occurred:")
         return
 
--- a/MoinMoin/action/Despam.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/action/Despam.py	Sun Mar 24 14:58:56 2013 +0100
@@ -204,6 +204,7 @@
     if (request.method == 'POST' and ok and
         wikiutil.checkTicket(request, request.form.get('ticket', ''))):
         revert_pages(request, editor, timestamp)
+        request.write(show_editors(request, pagename, timestamp))
     elif editor:
         show_pages(request, pagename, editor, timestamp)
     else:
--- a/MoinMoin/action/anywikidraw.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/action/anywikidraw.py	Sun Mar 24 14:58:56 2013 +0100
@@ -197,6 +197,8 @@
 
 def execute(pagename, request):
     target = request.values.get('target')
+    target = wikiutil.taintfilename(target)
+
     awd = AnyWikiDraw(request, pagename, target)
 
     do = request.values.get('do')
--- a/MoinMoin/action/cache.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/action/cache.py	Sun Mar 24 14:58:56 2013 +0100
@@ -27,6 +27,8 @@
     @license: GNU GPL, see COPYING for details.
 """
 
+from datetime import datetime
+
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
@@ -202,7 +204,7 @@
     """ 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)
     meta = meta_cache.content()
-    return meta['httpdate_last_modified'], meta['headers']
+    return meta['last_modified'], meta['headers']
 
 
 def _get_datafile(request, key):
@@ -216,7 +218,7 @@
     """ send a complete http response with headers/data cached for key """
     try:
         last_modified, headers = _get_headers(request, key)
-        if request.if_modified_since == last_modified:
+        if datetime.utcfromtimestamp(int(last_modified)) == request.if_modified_since:
             request.status_code = 304
         else:
             for k, v in headers:
--- a/MoinMoin/action/newaccount.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/action/newaccount.py	Sun Mar 24 14:58:56 2013 +0100
@@ -62,12 +62,11 @@
             return _("Password not acceptable: %s") % wikiutil.escape(pw_error)
 
     # Encode password
-    if password and not password.startswith('{SHA}'):
-        try:
-            theuser.enc_password = user.encodePassword(password)
-        except UnicodeError, err:
-            # Should never happen
-            return "Can't encode password: %s" % wikiutil.escape(str(err))
+    try:
+        theuser.enc_password = user.encodePassword(request.cfg, password)
+    except UnicodeError, err:
+        # Should never happen
+        return "Can't encode password: %s" % wikiutil.escape(str(err))
 
     # try to get the email, for new users it is required
     email = wikiutil.clean_input(form.get('email', ''))
--- a/MoinMoin/action/recoverpass.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/action/recoverpass.py	Sun Mar 24 14:58:56 2013 +0100
@@ -1,6 +1,6 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - create account action
+    MoinMoin - password recovery action
 
     @copyright: 2007 MoinMoin:JohannesBerg
     @license: GNU GPL, see COPYING for details.
@@ -104,6 +104,13 @@
 
     row = html.TR()
     tbl.append(row)
+    row.append(html.TD().append(html.STRONG().append(html.Text(_("Recovery token")))))
+    value = token or ''
+    row.append(html.TD().append(html.INPUT(type='text', size="36",
+                                           name="token", value=value)))
+
+    row = html.TR()
+    tbl.append(row)
     row.append(html.TD().append(html.STRONG().append(html.Text(_("Username")))))
     value = name or ''
     row.append(html.TD().append(html.INPUT(type='text', size="36",
@@ -111,13 +118,6 @@
 
     row = html.TR()
     tbl.append(row)
-    row.append(html.TD().append(html.STRONG().append(html.Text(_("Recovery token")))))
-    value = token or ''
-    row.append(html.TD().append(html.INPUT(type='text', size="36",
-                                           name="token", value=value)))
-
-    row = html.TR()
-    tbl.append(row)
     row.append(html.TD().append(html.STRONG().append(html.Text(_("New password")))))
     row.append(html.TD().append(html.INPUT(type="password", size="36",
                                            name="password")))
--- a/MoinMoin/action/twikidraw.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/action/twikidraw.py	Sun Mar 24 14:58:56 2013 +0100
@@ -208,6 +208,8 @@
 
 def execute(pagename, request):
     target = request.values.get('target')
+    target = wikiutil.taintfilename(target)
+
     twd = TwikiDraw(request, pagename, target)
 
     do = request.values.get('do')
--- a/MoinMoin/config/__init__.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/config/__init__.py	Sun Mar 24 14:58:56 2013 +0100
@@ -22,6 +22,16 @@
 # When creating files, we use e.g. 0666 & config.umask for the mode:
 umask = 0770
 
+# list of acceptable password hashing schemes for cfg.password_scheme,
+# here we only give reasonably good schemes, which is passlib (if we
+# have passlib) and ssha (if we only have builtin stuff):
+password_schemes_configurable = ['{PASSLIB}', '{SSHA}', ]
+
+# ordered list of supported password hashing schemes, best (passlib) should be
+# first, best builtin one should be second. this is what we support if we
+# encounter it in user profiles:
+password_schemes_supported = password_schemes_configurable + ['{SHA}', '{APR1}', '{MD5}', '{DES}', ]
+
 # Default value for the static stuff URL prefix (css, img, js).
 # Caution:
 # * do NOT use this directly, it is only the DEFAULT value to be used by
--- a/MoinMoin/config/multiconfig.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/config/multiconfig.py	Sun Mar 24 14:58:56 2013 +0100
@@ -427,6 +427,24 @@
                 raise error.ConfigurationError("You must set a (at least %d chars long) secret string for secrets['%s']!" % (
                     secret_min_length, secret_key_name))
 
+        if self.password_scheme not in config.password_schemes_configurable:
+            raise error.ConfigurationError("not supported: password_scheme = %r" % self.password_scheme)
+
+        if self.passlib_support:
+            try:
+                from passlib.context import CryptContext
+            except ImportError, err:
+                raise error.ConfigurationError("Wiki is configured to use passlib, but importing passlib failed [%s]!" % str(err))
+            try:
+                self.cache.pwd_context = CryptContext(**self.passlib_crypt_context)
+            except (ValueError, KeyError, TypeError, UserWarning), err:
+                # ValueError: wrong configuration values
+                # KeyError: unsupported hash (seen with passlib 1.3)
+                # TypeError: configuration value has wrong type
+                raise error.ConfigurationError("passlib_crypt_context configuration is invalid [%s]." % str(err))
+        elif self.password_scheme == '{PASSLIB}':
+            raise error.ConfigurationError("passlib_support is switched off, thus you can't use password_scheme = '{PASSLIB}'.")
+
     def calc_secrets(self):
         """ make up some 'secret' using some config values """
         varnames = ['data_dir', 'data_underlay_dir', 'language_default',
@@ -676,7 +694,8 @@
     # the options dictionary.
 
 
-def _default_password_checker(cfg, request, username, password):
+def _default_password_checker(cfg, request, username, password,
+                              min_length=6, min_different=4):
     """ Check if a password is secure enough.
         We use a built-in check to get rid of the worst passwords.
 
@@ -690,9 +709,9 @@
     """
     _ = request.getText
     # in any case, do a very simple built-in check to avoid the worst passwords
-    if len(password) < 6:
+    if len(password) < min_length:
         return _("Password is too short.")
-    if len(set(password)) < 4:
+    if len(set(password)) < min_different:
         return _("Password has not enough different characters.")
 
     username_lower = username.lower()
@@ -784,6 +803,35 @@
     ('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`)'),
 
+    ('password_scheme', '{PASSLIB}',
+     'Either "{PASSLIB}" (default) to use passlib for creating and upgrading password hashes (see also passlib_crypt_context for passlib configuration), '
+     'or "{SSHA}" (or any other of the builtin password schemes) to not use passlib (not recommended).'),
+
+    ('passlib_support', True,
+     'If True (default), import passlib and support password hashes offered by it.'),
+
+    ('passlib_crypt_context', dict(
+        # schemes we want to support (or deprecated schemes for which we still have
+        # hashes in our storage).
+        # note: bcrypt: we did not include it as it needs additional code (that is not pure python
+        #       and thus either needs compiling or installing platform-specific binaries) and
+        #       also there was some bcrypt issue in passlib < 1.5.3.
+        #       pbkdf2_sha512: not included as it needs at least passlib 1.4.0
+        #       sha512_crypt: supported since passlib 1.3.0 (first public release)
+        schemes=["sha512_crypt", ],
+        # default scheme for creating new pw hashes (if not given, passlib uses first from schemes)
+        #default="sha512_crypt",
+        # deprecated schemes get auto-upgraded to the default scheme at login
+        # time or when setting a password (including doing a moin account pwreset).
+        # for passlib >= 1.6, giving ["auto"] means that all schemes except the default are deprecated:
+        #deprecated=["auto"],
+        # to support also older passlib versions, rather give a explicit list:
+        #deprecated=[],
+        # vary rounds parameter randomly when creating new hashes...
+        #all__vary_rounds=0.1,
+    ),
+    "passlib CryptContext arguments, see passlib docs"),
+
   )),
   # ==========================================================================
   'spam_leech_dos': ('Anti-Spam/Leech/DOS',
@@ -1064,6 +1112,8 @@
      "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"),
+    ('log_events_format', 1,
+     "0 = no events logging, 1 = standard format (like <= 1.9.7) [default], 2 = extended format"),
 
     # 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
--- a/MoinMoin/i18n/MoinMoin.pot	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/MoinMoin.pot	Sun Mar 24 14:58:56 2013 +0100
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -424,12 +424,7 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
 msgstr ""
 
 msgid ""
@@ -441,7 +436,12 @@
 msgstr ""
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
 msgstr ""
 
 #, python-format
--- a/MoinMoin/i18n/ar.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/ar.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: moin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2011-01-10 17:16+0100\n"
 "Last-Translator: George Stephanos <gaf.stephanos@gmail.com>\n"
 "Language-Team: Arabic <moin-user@lists.sourceforge.net>\n"
@@ -457,18 +457,8 @@
 msgstr "<مجهول>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"اسم الولوج: %s\n"
-"\n"
-"رمز استرداد كلمة السر: %s\n"
-"\n"
-"رابط اعادة تعيين كلمة السر: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] معلومات حسابك بالويكي."
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -483,8 +473,18 @@
 "اذهب الي صفحة استرداد كلمة السر ثانياً و ضع اسم المستخدم و رمز الاسترداد.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] معلومات حسابك بالويكي."
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"اسم الولوج: %s\n"
+"\n"
+"رمز استرداد كلمة السر: %s\n"
+"\n"
+"رابط اعادة تعيين كلمة السر: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/bg.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/bg.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2011-01-06 22:08+0200\n"
 "Last-Translator: Krasi <wtfn00bz@ymail.com>\n"
 "Language-Team: Bulgarian <moin-user@lists.sourceforge.net>\n"
@@ -478,18 +478,8 @@
 msgstr "<неизвестен>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Потребителско име за вход: %s\n"
-"\n"
-"Жетон за възстановяване на парола: %s\n"
-"\n"
-"Адрес за рестартиране на парола: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Данни за Вашия уики акаунт"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -507,8 +497,18 @@
 "жетона за възстановяване на парола.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Данни за Вашия уики акаунт"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Потребителско име за вход: %s\n"
+"\n"
+"Жетон за възстановяване на парола: %s\n"
+"\n"
+"Адрес за рестартиране на парола: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/ca.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/ca.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: moin 1.7\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-07-30 17:24+0200\n"
 "Last-Translator: David Pinilla <dpini@caliu.net>\n"
 "Language-Team: Catalan <ca@dodds.net>\n"
@@ -482,18 +482,8 @@
 msgstr "<desconegut>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Nom d'entrada: %s\n"
-"\n"
-"Cadena de recuperació de contrasenya: %s\n"
-"\n"
-"URL de recuperació de contrasenya: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Les dades del vostre compte wiki"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -511,8 +501,18 @@
 "la contrasenya d'entrada.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Les dades del vostre compte wiki"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Nom d'entrada: %s\n"
+"\n"
+"Cadena de recuperació de contrasenya: %s\n"
+"\n"
+"URL de recuperació de contrasenya: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/cs.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/cs.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2011-01-03 20:51+0100\n"
 "Last-Translator: Petr Pecha <nejlepsitextovyeditorjevim@gmail.com>\n"
 "Language-Team: Czech <moin-user@lists.sourceforge.net>\n"
@@ -470,18 +470,8 @@
 msgstr "<neznámý>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Přihlašovací Jméno: %s\n"
-"\n"
-"Přihlašovací heslo: %s\n"
-"\n"
-"URL pro přihlášení: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s údaje o Vašem účtu"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -497,8 +487,18 @@
 "a token pro obnovu.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s údaje o Vašem účtu"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Přihlašovací Jméno: %s\n"
+"\n"
+"Přihlašovací heslo: %s\n"
+"\n"
+"URL pro přihlášení: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/da.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/da.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2011-01-06 23:21+0100\n"
 "Last-Translator: Jeppe Hallgren <jeppe@hallgren.com>\n"
 "Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
@@ -472,19 +472,8 @@
 msgstr "<ukendt>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Logindnavn: %s\n"
-"\n"
-"Symbol for adgangskodegendannelse: %s\n"
-"\n"
-"Adresse for nulstilling af adgangskode: %s?action=recoverpass&name=%s&token="
-"%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Dine wiki brugerprofil-oplysninger"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -502,8 +491,19 @@
 "billeten til kodeord-gendannelse.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Dine wiki brugerprofil-oplysninger"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Logindnavn: %s\n"
+"\n"
+"Symbol for adgangskodegendannelse: %s\n"
+"\n"
+"Adresse for nulstilling af adgangskode: %s?action=recoverpass&name=%s&token="
+"%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/de.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/de.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2010-03-27 02:46+0100\n"
 "Last-Translator: Thomas Waldmann <tw-public@gmx.de>\n"
 "Language-Team: German <moin-user@lists.sourceforge.net>\n"
@@ -480,18 +480,8 @@
 msgstr "<unbekannt>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Anmelde-Name: %s\n"
-"\n"
-"Passwort-Recovery-Token: %s\n"
-"\n"
-"Passwort-Rücksetz-URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Ihre Wiki-Acount-Daten"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -510,8 +500,18 @@
 "das Recovery-Token ein.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Ihre Wiki-Acount-Daten"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Anmelde-Name: %s\n"
+"\n"
+"Passwort-Recovery-Token: %s\n"
+"\n"
+"Passwort-Rücksetz-URL: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
@@ -1207,13 +1207,13 @@
 msgstr "Sprachpakete installieren für '%s'"
 
 msgid "TextCha: Wrong answer! Try again below..."
-msgstr ""
-"TextCha: Falsche Antwort! Probieren Sie es unten nochmal..."
+msgstr "TextCha: Falsche Antwort! Probieren Sie es unten nochmal..."
 
 msgid ""
 "Supplying a comment is mandatory.  Write a comment below and try again..."
 msgstr ""
-"Es ist zwingend erforderlich, einen Kommentar einzugeben.  Schreiben Sie unten einen Kommentar und probieren Sie es nochmal..."
+"Es ist zwingend erforderlich, einen Kommentar einzugeben.  Schreiben Sie "
+"unten einen Kommentar und probieren Sie es nochmal..."
 
 #, python-format
 msgid "[%d attachments]"
--- a/MoinMoin/i18n/el.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/el.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -11,29 +11,31 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: MoinMoin 1.6\n"
+"Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
-"PO-Revision-Date: 2011-01-06 00:04+0200\n"
-"Last-Translator: Giannis Konstantinidis <gloooabvoe9a@gmail.com>\n"
-"Language-Team: Greek <moin-devel@lists.sourceforge.net>\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
+"PO-Revision-Date: 2012-09-20 20:43+0300\n"
+"Last-Translator: Pantelis Koukousoulas <pktoss@gmail.com>\n"
+"Language-Team: Greek <moin-user@lists.sourceforge.net>\n"
 "Language: el\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Language: Ελληνικά\n"
 "X-Language-in-English: Greek\n"
 "X-HasWikiMarkup: True\n"
 "X-Direction: ltr\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.4\n"
 
 #, python-format
 msgid "The package needs a newer version of MoinMoin (at least %s)."
 msgstr ""
-"Το συγκεκριμένο πακέτα χρειάζεται μια πιο καινούρια έκδοση του MoinMoin "
+"Το συγκεκριμένο πακέτο χρειάζεται μια πιο καινούρια έκδοση του MoinMoin "
 "(τουλάχιστον την %s)."
 
 msgid "The theme name is not set."
-msgstr "Δεν έχει δωθεί όνομα για το θέμα."
+msgstr "Δεν έχει δοθεί όνομα για το θέμα."
 
 #, python-format
 msgid "Theme files not installed! Write rights missing for %s."
@@ -47,7 +49,7 @@
 
 #, python-format
 msgid "The file %s is not a MoinMoin package file."
-msgstr "Το αρχείο %s δεν έιναι ένα αρχείο πακέτου MoinMoin."
+msgstr "Το αρχείο %s δεν είναι ένα αρχείο πακέτου MoinMoin."
 
 #, python-format
 msgid "The page %s does not exist."
@@ -57,7 +59,7 @@
 msgstr "Λανθασμένη επικεφαλίδα πακέτου."
 
 msgid "Package file format unsupported."
-msgstr "Ο τύπος του πακέτου δεν υποστηρίζεται."
+msgstr "Το φορμά αρχείου του πακέτου δεν υποστηρίζεται."
 
 #, python-format
 msgid "Unknown function %(func)s in line %(lineno)i."
@@ -86,21 +88,21 @@
 #, python-format
 msgid "Argument \"%s\" must be a floating point value, not \"%s\""
 msgstr ""
-"Το όρισμα \"%s\" πρέπει να είναι τιμή τύπου κινητής υποδιαστολής και όχι \"%s"
+"Το όρισμα \"%s\" πρέπει να έχει τιμή τύπου κινητής υποδιαστολής και όχι \"%s"
 "\""
 
 #, python-format
 msgid "Argument must be a floating point value, not \"%s\""
 msgstr ""
-"Το όρισμα πρέπει να είναι τιμή τύπου κινητής υποδιαστολής και όχι \"%s\""
+"Το όρισμα πρέπει να έχει τιμή τύπου κινητής υποδιαστολής και όχι \"%s\""
 
 #, python-format
 msgid "Argument \"%s\" must be a complex value, not \"%s\""
-msgstr "Το όρισμα \"%s\" πρέπει να είναι σύνθετη τιμή και όχι \"%s\""
+msgstr "Το όρισμα \"%s\" πρέπει να έχει σύνθετη τιμή και όχι \"%s\""
 
 #, python-format
 msgid "Argument must be a complex value, not \"%s\""
-msgstr "Το όρισμα πρέπει να είναι σύνθετη τιμή και όχι \"%s\""
+msgstr "Το όρισμα πρέπει να έχει σύνθετη τιμή και όχι \"%s\""
 
 #, python-format
 msgid "Argument \"%s\" must be one of \"%s\", not \"%s\""
@@ -111,7 +113,7 @@
 msgstr "Το όρισμα πρέπει να είναι ένα από τα \"%s\", και όχι \"%s\""
 
 msgid "Too many arguments"
-msgstr "Πολλά ορίσματα"
+msgstr "Υπερβολικά πολλά ορίσματα"
 
 msgid "Cannot have arguments without name following named arguments"
 msgstr ""
@@ -138,10 +140,10 @@
 msgstr "Δεν επιτρέπεται να επεξεργαστείτε αυτή τη σελίδα."
 
 msgid "Page is immutable!"
-msgstr "H σελίδα είναι απροσπέλαστη!"
+msgstr "Η σελίδα δεν επιδέχεται μεταβολές!"
 
 msgid "Cannot edit old revisions!"
-msgstr "Δε μπορεί να γίνει επεξεργασία σε παλιότερες revisions!"
+msgstr "Δε μπορεί να γίνει επεξεργασία σε παλιότερες αναθεωρήσεις!"
 
 msgid "The lock you held timed out. Be prepared for editing conflicts!"
 msgstr ""
@@ -176,18 +178,18 @@
 "δευτερόλεπτα."
 
 msgid "Someone else deleted this page while you were editing!"
-msgstr "Κάποιος άλλος διέγραψε τη σελίδα ενώ την επεξεργαζόσασταν!"
+msgstr "Κάποιος άλλος διέγραψε αυτή τη σελίδα ενώ την επεξεργαζόσασταν!"
 
 msgid "Someone else changed this page while you were editing!"
-msgstr "Κάποιος άλλος άλλαξε τη σελίδα ενώ την επεξεργαζόσασταν!"
+msgstr "Κάποιος άλλος άλλαξε αυτή τη σελίδα ενώ την επεξεργαζόσασταν!"
 
 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 ""
-"Κάποιος άλλος αποθήκευσε τη σελίδα ενώ την επεξεργαζόσασταν!\n"
+"Κάποιος άλλος αποθήκευσε αυτή τη σελίδα ενώ την επεξεργαζόσασταν!\n"
 "Παρακαλώ κάντε μια ανασκόπηση στη σελίδα και ύστερα αποθηκεύστε την. Μην την "
-"αποθηκεύεται ως έχει!"
+"αποθηκεύετε ως έχει!"
 
 msgid "[Content loaded from draft]"
 msgstr "[Το περιεχόμενο φορτώθηκε από το προσχέδιο]"
@@ -198,11 +200,11 @@
 
 #, python-format
 msgid "[Template %s not found]"
-msgstr "[Το πρότυπο %s δεν ήταν δυνατόν να βρεθεί]"
+msgstr "[Το πρότυπο %s δεν βρέθηκε]"
 
 #, python-format
 msgid "[You may not read %s]"
-msgstr "[Δε γίνεται να διαβάσετε το %s]"
+msgstr "[Δε επιτρέπεται να διαβάσετε το %s]"
 
 #, python-format
 msgid ""
@@ -212,6 +214,12 @@
 "edit somehow without saving it.''' A draft gets saved for you when you do a "
 "preview, cancel an edit or unsuccessfully save."
 msgstr ""
+"'''<<BR>>Το προσχέδιό σας βασισμένο στην αναθεώρηση %(draft_rev)d (saved "
+"%(draft_timestamp_str)s) μπορεί να φορτωθεί αντί για την τρέχουσα αναθεώρηση "
+"%(page_rev)d χρησιμοποιώντας το κουμπί φόρτωση προσχεδίου - σε περίπτωση που "
+"χάσατε την τελευταία σας επεξεργασία χωρίς να τη σώσετε.''' Ένα προσχέδιο "
+"αποθηκεύεται για εσάς κάθε φορά που κάνετε προεπισκόπηση, ακυρώνετε μια "
+"αλλαγή ή αποθηκεύετε ανεπιτυχώς."
 
 #, python-format
 msgid "Describe %s here."
@@ -233,6 +241,10 @@
 "If you don't want that, hit '''%(cancel_button_text)s''' to cancel your "
 "changes."
 msgstr ""
+"Πατώντας '''%(save_button_text)s''' οι αλλαγές σας τίθενται υπό την άδεια "
+"%(license_link)s.\n"
+"Αν δεν το επιθυμείτε αυτό, πατήστε '''%(cancel_button_text)s''' για να "
+"ακυρώσετε τις αλλαγές σας."
 
 msgid "Preview"
 msgstr "Προεπισκόπηση"
@@ -241,10 +253,10 @@
 msgstr "Λειτουργία Κειμένου"
 
 msgid "Load Draft"
-msgstr "Φόρτωσε το Πρότυπο"
+msgstr "Φόρτωσε το Προσχέδιο"
 
 msgid "Trivial change"
-msgstr "Ασήμαντη αλλαγή"
+msgstr "Τετριμμένη αλλαγή"
 
 msgid "Comment:"
 msgstr "Σχόλιο:"
@@ -261,11 +273,11 @@
 
 #, python-format
 msgid "Unknown action %(action_name)s."
-msgstr "Άγνωστη εντολή %(action_name)s."
+msgstr "Άγνωστη ενέργεια %(action_name)s."
 
 #, python-format
 msgid "You are not allowed to do %(action_name)s on this page."
-msgstr "Δεν επιτρέπεται να κάνετε %(action_name)s σε αυτη τη σελίδα."
+msgstr "Δεν επιτρέπεται να κάνετε %(action_name)s σε αυτή τη σελίδα."
 
 msgid "Login and try again."
 msgstr "Παρακαλώ συνδεθείτε και προσπαθήστε ξανά."
@@ -281,10 +293,12 @@
 "The remote wiki uses a different InterWiki name (%(remotename)s) internally "
 "than you specified (%(localname)s)."
 msgstr ""
+"Το απομακρυσμένο wiki χρησιμοποιεί ένα διαφορετικό InterWiki όνομα "
+"(%(remotename)s) εσωτερικά από αυτό που καθορίσατε (%(localname)s)."
 
 #, python-format
 msgid "Invalid highlighting regular expression \"%(regex)s\": %(error)s"
-msgstr ""
+msgstr "Λανθασμένη κανονική έκφραση τονισμού \"%(regex)s\": %(error)s"
 
 #, python-format
 msgid ""
@@ -299,12 +313,12 @@
 "The backed up content of this page is deprecated and will rank lower in "
 "search results!"
 msgstr ""
-"Το περιεχόμενο αντιγράφων ασφαλείας αυτης της σελίδας έχει καταργηθεί και θα "
-"μετρήσει λιγότερο στα αποτελέσματα αναζήτησης!"
+"Το περιεχόμενο αντιγράφων ασφαλείας αυτής της σελίδας είναι παρωχημένο και "
+"θα μετρήσει λιγότερο στα αποτελέσματα αναζήτησης!"
 
 #, python-format
 msgid "Revision %(rev)d as of %(date)s"
-msgstr "Νέα έκδοση %(rev)d από τις %(date)s"
+msgstr "Νέα αναθεώρηση %(rev)d από τις %(date)s"
 
 #, python-format
 msgid "Redirected from page \"%(page)s\""
@@ -312,7 +326,7 @@
 
 #, python-format
 msgid "This page redirects to page \"%(page)s\""
-msgstr "Αυτή η σελίδα ανακατευθύνεται στην \"%(page)s\""
+msgstr "Αυτή η σελίδα ανακατευθύνει στην \"%(page)s\""
 
 msgid "Create New Page"
 msgstr "Δημιουργία Νέας Σελίδας"
@@ -329,7 +343,7 @@
 "μικρότερο."
 
 msgid "GUI Mode"
-msgstr "Παραθυρική Λειτουργία"
+msgstr "Λειτουργία Γραφικού Περιβάλλοντος"
 
 msgid "Edit was cancelled."
 msgstr "Η διαδικασία της επεξεργασίας ακυρώθηκε."
@@ -364,8 +378,7 @@
 #, python-format
 msgid "Could not rename page because of file system error: %s."
 msgstr ""
-"Η σελίδα δεν μπορούσε να αλλάξει ονομασία λόγω σφάλματος στη βάση δεδομένων: "
-"%s"
+"Η σελίδα δεν μπορούσε να μετονομαστεί λόγω σφάλματος στο σύστημα αρχείων: %s"
 
 msgid "You are not allowed to delete this page!"
 msgstr "Δεν επιτρέπεται να διαγράψετε αυτή τη σελίδα!"
@@ -376,33 +389,34 @@
 
 #, python-format
 msgid "Page \"%s\" was successfully deleted!"
-msgstr "Η σελίδα \"%s\" διαγράφτηκε επιτυχώς!"
+msgstr "Η σελίδα \"%s\" διαγράφηκε επιτυχώς!"
 
 #, python-format
 msgid "Page could not get locked. Unexpected error (errno=%d)."
 msgstr "Η σελίδα δε γίνεται να κλειδωθεί. Απρόσμενο λάθος (errno=%d)."
 
 msgid "Page could not get locked. Missing 'current' file?"
-msgstr "Η σελίδα δε γίνεται να κλειδωθεί. Δεν υπάρχει το 'τρέχον' αρχείο;"
+msgstr "Η σελίδα δε γίνεται να κλειδωθεί. Δεν υπάρχει το 'current' αρχείο;"
 
 #, 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 ""
-"Δεν είναι δυνατόν να υπολογισθεί η έκδοση της σελίδας από το 'τρέχον' "
-"αρχείο. Η σελίδα %s έχει καταστραφεί και δε είναι δυνατόν αυτή τη στιγμή να "
+"Δεν είναι δυνατόν να υπολογισθεί η έκδοση της σελίδας από το 'current' "
+"αρχείο. Η σελίδα %s έχει καταστραφεί και δεν είναι δυνατόν αυτή τη στιγμή να "
 "την επεξεργαστείτε."
 
 #, python-format
 msgid "Cannot save page %s, no storage space left."
-msgstr "Δε γίνεται να αποθηκευθεί η σελίδα %s, λόγω έλλειψης χώρου."
+msgstr ""
+"Δε γίνεται να αποθηκευθεί η σελίδα %s, λόγω έλλειψης χώρου αποθήκευσης."
 
 #, python-format
 msgid "An I/O error occurred while saving page %s (errno=%d)"
 msgstr ""
-"Συνέβη ένα σφάλμα Ε/Ε (I/O) ενώ γινόταν προσπάθεια αποθήκευσης της σελίδας "
-"%s (errno=%d)"
+"Ένα Ι/Ο σφάλμα προέκυψε ενώ γινόταν προσπάθεια αποθήκευσης της σελίδας %s "
+"(errno=%d)"
 
 msgid "You are not allowed to edit this page!"
 msgstr "Δεν επιτρέπεται να επεξεργαστείτε αυτή τη σελίδα!"
@@ -415,8 +429,8 @@
 
 msgid "You already edited this page! Please do not use the back button."
 msgstr ""
-"Έχετε ήδη επεξεργασθεί αυτή τη σελίδα! Σας παρακαλούμε να μη χρησιμοποιείται "
-"το πίσω κουμπί."
+"Έχετε ήδη επεξεργασθεί αυτή τη σελίδα! Σας παρακαλούμε να μη χρησιμοποιείτε "
+"το κουμπί 'πίσω'."
 
 msgid "You did not change the page content, not saved!"
 msgstr "Δεν αποθηκεύτηκε, γιατί δεν αλλάξατε το περιεχόμενο της σελίδας!"
@@ -428,7 +442,7 @@
 "δικαιώματα διαχειριστή!"
 
 msgid "Notifications sent to:"
-msgstr "Οι υπενθιμίσεις στάλθηκαν σε:"
+msgstr "Οι υπενθυμίσεις στάλθηκαν σε:"
 
 #, python-format
 msgid ""
@@ -436,13 +450,13 @@
 "granted the lock for this page."
 msgstr ""
 "Το κλείδωμα του %(owner)s έληξε πριν από %(mins_ago)d λεπτό(α) και έχετε "
-"πλέον εσείς τον έλεγχο αυτης της σελίδας."
+"πλέον εσείς τον έλεγχο αυτής της σελίδας."
 
 #, python-format
 msgid ""
 "Other users will be ''blocked'' from editing this page until %(bumptime)s."
 msgstr ""
-"Άλλοι χρήστες θα ''απαγορευτούν'' από το να επεξεργάζονται αυτη τη σελίδα "
+"Άλλοι χρήστες θα ''απαγορευτούν'' από το να επεξεργάζονται αυτή τη σελίδα "
 "έως %(bumptime)s."
 
 #, python-format
@@ -450,12 +464,12 @@
 "Other users will be ''warned'' until %(bumptime)s that you are editing this "
 "page."
 msgstr ""
-"Άλλοι χρήστες θα ''προειδοποιηθούν'' έως %(bumptime)s οτι κάνετε επεξεργασία "
-"σε αυτη τη σελίδα."
+"Οι άλλοι χρήστες θα ''προειδοποιούνται'' έως %(bumptime)s ότι κάνετε "
+"επεξεργασία σε αυτή τη σελίδα."
 
 msgid "Use the Preview button to extend the locking period."
 msgstr ""
-"Χρησιμοποιήστε το πλήκτρο Προεπισκόπιση για να επεκτείνετε την περίοδο "
+"Χρησιμοποιήστε το πλήκτρο Προεπισκόπηση για να επεκτείνετε την περίοδο "
 "κλειδώματος"
 
 #, python-format
@@ -463,7 +477,7 @@
 "This page is currently ''locked'' for editing by %(owner)s until "
 "%(timestamp)s, i.e. for %(mins_valid)d minute(s)."
 msgstr ""
-"Αυτη η σελίδα είναι προσωρινά ''κλειδωμένη'' για επεξεργασία από %(owner)s "
+"Αυτή η σελίδα είναι προσωρινά ''κλειδωμένη'' για επεξεργασία από %(owner)s "
 "μέχρι %(timestamp)s, δηλαδή για %(mins_valid)d λεπτό(α)."
 
 #, python-format
@@ -475,11 +489,36 @@
 "to avoid editing conflicts.'''<<BR>>\n"
 "To leave the editor, press the Cancel button."
 msgstr ""
+"Αυτή η σελίδα ανοίχτηκε για επεξεργασία ή τελευταία προεπισκόπηση στις "
+"%(timestamp)s από %(owner)s.><<BR>>\n"
+"'''Θα πρέπει να \"αποφύγετε την επεξεργασία\" αυτής της σελίδας για "
+"τουλάχιστον ακόμα %(mins_valid)d λεπτό(α).\n"
+"για να αποφύγετε συγκρούσεις.'''<<BR>>\n"
+"Για να κλείσετε την επεξεργασία, πατήστε το κουμπί Ακύρωση"
 
 msgid "<unknown>"
 msgstr "<άγνωστο>"
 
-#, fuzzy, python-format
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Οι πληροφορίες του wiki λογαριασμού σας"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+"Κάποιος ζήτησε να σας αποσταλεί ένα ηλεκτρονικό token για την ανάκτηση του "
+"κωδικού σας.\n"
+"\n"
+"Εάν χάσατε τον κωδικό σας, παρακαλώ ακολουθήστε τον παρακάτω σύνδεσμο "
+"ανάκτησης ή\n"
+"πηγαίνετε ξανά στη σελίδα ανάκτησης κωδικού και γράψτε το όνομα χρήστη και\n"
+"το token ανάκτησης.\n"
+
+#, python-format
 msgid ""
 "Login Name: %s\n"
 "\n"
@@ -489,41 +528,22 @@
 msgstr ""
 "Όνομα εισόδου: %s\n"
 "\n"
-"Κωδικός εισόδου: %s\n"
-"\n"
-"Σελίδα εισόδου: %s/%s?action=login\n"
-
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
+"Token ανάκτησης κωδικού: %s\n"
 "\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-"Κάποιος ζήτηση να σας αποσταλεί ένα ηλεκτρονικό μήνυμα για την ανάκτηση του "
-"κωδικού σας.\n"
-"\n"
-"Εάν χάσατε τον κωδικό σας, παρακαλώ ακολουθήστε τον παρακάτω σύνδεσμο "
-"ανάκτησης ή\n"
-"πηγαίνετε ξανά στη σελίδα ανάκτησης κωδικού και γράψτε το όνομα χρήστη και\n"
-"τον κωδικό ανάκτησης.\n"
+"URL για να θέσετε νέο κωδικό: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Οι πληροφορίες του wiki λογαριασμού σας"
-
-#, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
-msgstr "Πρέπει να συνδεθείτε για να προσθέσετε ένα quicklink."
+msgstr "Πρέπει να συνδεθείτε για να χρησιμοποιήσετε την ενέργεια: %(action)s."
 
 msgid "Your subscription to this page has been removed."
 msgstr "Η συνδρομή σας σε αυτή τη σελίδα έχει αφαιρεθεί."
 
 msgid "Can't remove regular expression subscription!"
-msgstr "Αδυναμία αφαίρεσης της τακτικής συνδρομής!"
+msgstr "Αδυναμία αφαίρεσης της συνδρομής σε κανονική έκφραση!"
 
 msgid "Edit the subscription regular expressions in your settings."
-msgstr "Επεξεργασία της τακτικής συνδρομής στις ρυθμίσεις σας."
+msgstr "Επεξεργασία των κανονικών εκφράσεων της συνδρομής στις ρυθμίσεις σας."
 
 msgid "You need to be subscribed to unsubscribe."
 msgstr "Πρέπει πρώτα να έχετε κάνει συνδρομή ώστε να τη διακόψετε."
@@ -543,21 +563,25 @@
 #, python-format
 msgid "Please use the interactive user interface to use action %(actionname)s!"
 msgstr ""
+"Παρακαλώ χρησιμοποιήστε το αλληλεπιδραστικό περιβάλλον για να "
+"χρησιμοποιήσετε την ενέργεια  %(actionname)s!"
 
 msgid "You are not allowed to save a drawing on this page."
-msgstr "Δεν έχετε το δικαίωμα να αποθηκεύσετε ένα σχέδιο σε αυτη τη σελίδα."
+msgstr "`Δεν έχετε το δικαίωμα να αποθηκεύσετε ένα σχέδιο σε αυτή τη σελίδα."
 
 msgid "Empty target name given."
-msgstr "Έχει δωθεί λάθος όνομα στόχου."
+msgstr "Έχει δοθεί λάθος όνομα στόχου."
 
 msgid ""
 "No file content. Delete non ASCII characters from the file name and try "
 "again."
 msgstr ""
+"Αρχείο χωρίς περιεχόμενο. Σβήστε όλους τους μη ASCII χαρακτήρες από το όνομα "
+"του αρχείου και δοκιμάστε ξανά."
 
 msgid "You are not allowed to view attachments of this page."
 msgstr ""
-"Δεν επιτρέπεται να προβάλλετε τα επισυναπτόμενα αρχεία αυτης της σελίδας."
+"Δεν επιτρέπεται να προβάλλετε τα επισυναπτόμενα αρχεία αυτής της σελίδας."
 
 msgid "Edit drawing"
 msgstr "Επεξεργασία σχεδίασης"
@@ -566,7 +590,7 @@
 msgstr "Δεν υπάρχουν παλαιότερες εκδόσεις διαθέσιμες!"
 
 msgid "No log entries found."
-msgstr "Δεν βρέθηκαν καταχωρήσεις στο πρακτικό"
+msgstr "Δεν βρέθηκαν καταχωρήσεις στο αρχείο καταγραφής."
 
 #, python-format
 msgid "Diff for \"%s\""
@@ -574,14 +598,14 @@
 
 #, python-format
 msgid "Differences between revisions %d and %d"
-msgstr "Διαφορές ανάμεσα στις εκδόσεις %d και %d"
+msgstr "Διαφορές ανάμεσα στις αναθεωρήσεις %d και %d"
 
 #, python-format
 msgid "(spanning %d versions)"
-msgstr "(σύνδεση %d εκδόσεων)"
+msgstr "(κάλυψη %d εκδόσεων)"
 
 msgid "Revert to this revision"
-msgstr "Επιστροφή σε αυτη την έκδοση"
+msgstr "Επαναφορά σε αυτή την έκδοση"
 
 msgid "Previous change"
 msgstr "Προηγούμενη αλλαγή"
@@ -593,7 +617,7 @@
 msgstr "Μέγεθος"
 
 msgid "Editor"
-msgstr "Επεξεργαστής"
+msgstr "Συντάκτης"
 
 msgid "Date"
 msgstr "Ημερομηνία"
@@ -617,14 +641,15 @@
 msgstr "Δεν μπορεί να γίνει αλλαγή σε ενημέρωση νεότερη από το δεξί παράθυρο"
 
 msgid "N/A"
-msgstr "Δ/Α"
+msgstr "N/A"
 
 msgid "Diff with older revision in right pane"
 msgstr "Diff με παλαιότερη ενημέρωση στο δεξί παράθυρο"
 
 msgid "Can't change to revision older than revision in left pane"
 msgstr ""
-"Δεν μπορεί να γίνει αλλαγή σε ενημέρωση παλαιότερη από το αριστερό παράθυρο"
+"Δεν μπορεί να γίνει αλλαγή σε ενημέρωση παλαιότερη από την ενημέρωση στο "
+"αριστερό παράθυρο"
 
 msgid "Diff with newer revision in right pane"
 msgstr "Diff με νεότερη ενημέρωση στο δεξί παράθυρο"
@@ -643,10 +668,10 @@
 msgstr "Μα, η σελίδα αυτή έχει αποθηκευθεί %(count)d φορές!"
 
 msgid "(ignoring whitespace)"
-msgstr "(το κενό αγνοείται)"
+msgstr "(τα κενά αγνοούνται)"
 
 msgid "Ignore changes in the amount of whitespace"
-msgstr "Αγνόηση αλλαγών στο πλήθος των κενών"
+msgstr "Παράβλεψη αλλαγών στο πλήθος των κενών"
 
 #, python-format
 msgid "Full Link List for \"%s\""
@@ -661,6 +686,8 @@
 "The following %(badwords)d words could not be found in the dictionary of "
 "%(totalwords)d words%(localwords)s and are highlighted below:"
 msgstr ""
+"Οι ακόλουθες %(badwords)d λέξεις δεν ήταν δυνατόν να βρεθούν στο λεξικό των "
+"%(totalwords)d λέξεων %(localwords)s και τονίζονται παρακάτω:"
 
 msgid "Add checked words to dictionary"
 msgstr "Προσθήκη επιλεγμένων λέξεων στο λεξικό"
@@ -673,31 +700,31 @@
 
 msgid "You can't check spelling on a page you can't read."
 msgstr ""
-"Δεν μπορείτε να κάνετε έλεγχο ορθογραφίας σε μια σελίδα που δεν μπορείτε να "
-"διαβάσετε."
+"Δεν μπορείτε να κάνετε έλεγχο ορθογραφίας σε μια σελίδα που δεν επιτρέπεται "
+"να διαβάσετε."
 
 msgid "Do it."
-msgstr "Καν το."
+msgstr "Κανε το."
 
 #, python-format
 msgid "Execute action %(actionname)s?"
-msgstr "Εκτέλεση της εντολής %(actionname)s;"
+msgstr "Εκτέλεση ενέργειας %(actionname)s;"
 
 #, python-format
 msgid "Action %(actionname)s is excluded in this wiki!"
-msgstr "Η εντολή %(actionname)s περιλαμβάνεται σε αυτο το wiki!"
+msgstr "Η ενέργεια %(actionname)s έχει εξαιρεθεί από αυτό το wiki!"
 
 #, python-format
 msgid "You are not allowed to use action %(actionname)s on this page!"
 msgstr ""
-"Δεν έχετε το δικαίωμα να εκτελέσετε το %(actionname)s σε αυτη τη σελίδα!"
+"Δεν έχετε το δικαίωμα να εκτελέσετε το %(actionname)s σε αυτή τη σελίδα!"
 
 msgid "Please log in first."
 msgstr "Παρακαλώ συνδεθείτε πρώτα."
 
 msgid "Please first create a homepage before creating additional pages."
 msgstr ""
-"Παρακαλώ δημιουργήστε μια κεντρική σελίδα πρωτού δημιουργήσετε επιπλέον "
+"Παρακαλώ δημιουργήστε μια κεντρική σελίδα προτού δημιουργήσετε επιπλέον "
 "σελίδες."
 
 #, python-format
@@ -727,9 +754,33 @@
 "%(username)s only||\n"
 "\n"
 msgstr ""
+"Μπορείτε να προσθέσετε μερικές επιπλέον σελίδες στην ήδη υπάρχουσα κεντρική "
+"σελίδα σας εδώ.\n"
+"\n"
+"Μπορείτε να επιλέξετε πόσο ανοιχτές σε άλλους αναγνώστες ή συντάκτες θα "
+"είναι αυτές οι σελίδες,\n"
+"η πρόσβαση περιορίζεται στα μέλη της σελίδας του αντίστοιχου group.\n"
+"\n"
+"Απλά εισάγετε το όνομα της υπο-σελίδας και κάντε κλικ στο κουμπί δημιουργίας "
+"νέας σελίδας\n"
+"\n"
+"Πριν δημιουργήσετε σελίδες περιορισμένης πρόσβασης, σιγουρευτείτε ότι η "
+"αντίστοιχη group σελίδα\n"
+"υπάρχει και έχει τα επιθυμητά μέλη. Χρησιμοποιήστε το  "
+"HomepageGroupsTemplate  για να φτιάξετε\n"
+"τις σελίδες των group.\n"
+"\n"
+"||'''Νέα προσωπική σελίδα:'''||'''Σχετικό group για έλεγχο πρόσβασης:'''||\n"
+"||<<NewPage(HomepageReadWritePageTemplate,read-write page,%(username)s)>>||"
+"[[%(username)s/ReadWriteGroup]]||\n"
+"||<<NewPage(HomepageReadPageTemplate,read-only page,%(username)s)>>||"
+"[[%(username)s/ReadGroup]]||\n"
+"||<<NewPage(HomepagePrivatePageTemplate,private page,%(username)s)>>||"
+"%(username)s only||\n"
+"\n"
 
 msgid "MyPages management"
-msgstr "Διαχείρηση MyPages"
+msgstr "Διαχείριση MyPages"
 
 msgid "Login"
 msgstr "Είσοδος"
@@ -745,16 +796,18 @@
 msgstr "Το SHA digest του περιεχομένου της σελίδας είναι:"
 
 msgid "The following users subscribed to this page:"
-msgstr "Οι παρακάτω χρήστες έχουν κάνει συνδρομή σε αυτη τη σελίδα:"
+msgstr "Οι παρακάτω χρήστες έχουν κάνει συνδρομή σε αυτή τη σελίδα:"
 
 msgid "This page links to the following pages:"
-msgstr "Αυτη η σελίδα συνδέεται με τις παρακάτω σελίδες:"
+msgstr "Αυτή η σελίδα συνδέεται με τις παρακάτω σελίδες:"
 
 #, python-format
 msgid ""
 "Showing page edit history entries from '''%(start_offset)d''' to "
 "'''%(end_offset)d''' out of '''%(total_count)d''' entries total."
 msgstr ""
+"Εμφάνιση ιστορικών καταχωρήσεων αλλαγών σελίδας από  '''%(start_offset)d''' "
+"ως '''%(end_offset)d''' από '''%(total_count)d''' καταχωρήσεις συνολικά."
 
 msgid "Newer"
 msgstr "Νεότερο"
@@ -764,13 +817,13 @@
 
 #, python-format
 msgid "%s items per page"
-msgstr "%s αντικείμενα ανα σελίδα"
+msgstr "%s αντικείμενα ανά σελίδα"
 
 msgid "Diff"
 msgstr "Diff"
 
 msgid "Action"
-msgstr "Δράση"
+msgstr "Ενέργεια"
 
 msgid "view"
 msgstr "προβολή"
@@ -816,10 +869,10 @@
 msgstr "Αντιγραφή Σελίδας"
 
 msgid "This page is already deleted or was never created!"
-msgstr "Αυτη η σελίδα διαγράφτηκε ήδη ή δεν δημιουργήθηκε ποτέ!"
+msgstr "Αυτή η σελίδα διαγράφτηκε ήδη ή δεν δημιουργήθηκε ποτέ!"
 
 msgid "TextCha: Wrong answer! Go back and try again..."
-msgstr "TextCha: Λάθος απάντηση! Πηγαίντε πίσω και ξανά-προσπαθήστε..."
+msgstr "TextCha: Λάθος απάντηση! Πηγαίνετε πίσω και ξανά-προσπαθήστε..."
 
 msgid "Copy all /subpages too?"
 msgstr "Να αντιγραφούν επίσης όλες οι /subpages;"
@@ -828,7 +881,7 @@
 msgstr "Νέα ονομασία"
 
 msgid "Optional reason for the copying"
-msgstr "Προεραιτικός λόγος αντιγραφής"
+msgstr "Προαιρετικός λόγος αντιγραφής"
 
 msgid "Really copy this page?"
 msgstr "Θέλετε σίγουρα να αντιγράφει αυτή η σελίδα;"
@@ -840,29 +893,34 @@
 msgstr "Να διαγραφούν επίσης όλες οι /subpages;"
 
 msgid "Optional reason for the deletion"
-msgstr "Προεραιτικός λόγος διαγραφής"
+msgstr "Προαιρετικός λόγος διαγραφής"
 
 msgid "Really delete this page?"
 msgstr "Να διαγραφεί σίγουρα η σελίδα;"
 
 #, python-format
 msgid "Rolled back changes to the page %s."
-msgstr "Επαναφορά των αλλαγώς στη σελίδα %s."
+msgstr "Επαναφορά των αλλαγών στη σελίδα %s."
 
 msgid "Exception while calling rollback function:"
 msgstr ""
+"Εξαίρεση κατά τη διάρκεια κλήσης της συνάρτησης επαναφοράς σε προηγούμενη "
+"έκδοση:"
 
 msgid ""
 "Please enter your password of your account at the remote wiki below. "
 "<<BR>> /!\\ You should trust both wikis because the password could be read "
 "by the particular administrators."
 msgstr ""
+"Παρακαλώ εισάγετε τον κωδικό πρόσβασης του λογαριασμού σας στο απομακρυσμένο "
+"wiki παρακάτω.<<B.R>> /!\\ Θα πρέπει να εμπιστεύεστε και τα 2 wikis γιατί ο "
+"κωδικός πρόσβασης μπορεί να διαβαστεί από τους σχετικούς διαχειριστές"
 
 msgid "Name"
 msgstr "Όνομα"
 
 msgid "Password"
-msgstr "Κωδικός"
+msgstr "Κωδικός πρόσβασης"
 
 msgid "Operation was canceled."
 msgstr "Η λειτουργία ακυρώθηκε."
@@ -875,7 +933,7 @@
 "be able to use this action."
 msgstr ""
 "Παρακαλώ δώστε ένα όνομα wiki στις ρυθμίσεις του wiki (δείτε "
-"HelpOnConfiguration) για να μπορείτε να χρησιμοποιήσετε αυτη την ενέργεια."
+"HelpOnConfiguration) για να μπορείτε να χρησιμοποιήσετε αυτή την ενέργεια."
 
 msgid ""
 "Incorrect parameters. Please supply at least the ''remoteWiki'' parameter. "
@@ -903,7 +961,7 @@
 "process."
 msgstr ""
 "Έλαβε μια λίστα από τις %s τοπικές και τις %s απομακρυσμένες σελίδες. Αυτό "
-"έχει ώς αποτέλεσμα σε %s σελίδες προς επεξεργασία."
+"έχει ως αποτέλεσμα σε %s σελίδες προς επεξεργασία."
 
 #, python-format
 msgid "After filtering: %s pages"
@@ -912,7 +970,7 @@
 #, python-format
 msgid "Skipped page %s because of no write access to local page."
 msgstr ""
-"Προσπεράστηκε η σελίδα %s καθώς δεν υπάρχουν δικαιώματα εγγραφής στην τοπική "
+"Παραλείφθηκε η σελίδα %s καθώς δεν υπάρχουν δικαιώματα εγγραφής στην τοπική "
 "σελίδα."
 
 #, python-format
@@ -921,7 +979,7 @@
 
 #, python-format
 msgid "Error while deleting page %s locally:"
-msgstr "Σφάλμα κατα την τοπική διαγραφή της σελίδας %s:"
+msgstr "Σφάλμα κατά την τοπική διαγραφή της σελίδας %s:"
 
 #, python-format
 msgid "Deleted page %s remotely."
@@ -929,25 +987,32 @@
 
 #, python-format
 msgid "Error while deleting page %s remotely:"
-msgstr "Σφάλμα κατα τη διάρκεια της απομακρυσμένης διαγραφής της σελίδας %s:"
+msgstr "Σφάλμα κατά τη διάρκεια της απομακρυσμένης διαγραφής της σελίδας %s:"
 
 #, python-format
 msgid ""
 "The item %s cannot be merged automatically but was changed in both wikis. "
 "Please delete it in one of both wikis and try again."
 msgstr ""
+"Το αντικείμενο %s δεν μπορεί να συνενωθεί αυτόματα αλλά άλλαξε και στα δύο "
+"wikis. Παρακαλώ διαγράψτε το στο ένα από τα 2  wikis και ξαναδοκιμάστε."
 
 #, python-format
 msgid ""
 "The item %s has different mime types in both wikis and cannot be merged. "
 "Please delete it in one of both wikis or unify the mime type, and try again."
 msgstr ""
+"Το αντικείμενο %s έχει διαφορετικούς τύπους MIME στα δύο wikis και δεν "
+"μπορεί να συνενωθεί. Παρακαλούμε διαγράψτε το στο ένα ή και στα 2 wikis ή "
+"ενοποιήστε τον τύπο MIME και ξαναδοκιμάστε"
 
 #, python-format
 msgid ""
 "The item %s was renamed locally. This is not implemented yet. Therefore the "
 "full synchronisation history is lost for this page."
 msgstr ""
+"Το αντικείμενο %s μετονομάστηκε τοπικά. Αυτό δεν έχει υλοποιηθεί ακόμα. "
+"Συνεπώς το πλήρες ιστορικό συγχρονισμού έχει χαθεί γι αυτή τη σελίδα."
 
 #, python-format
 msgid "Synchronising page %s with remote page %s ..."
@@ -955,13 +1020,16 @@
 
 #, python-format
 msgid "The page %s was deleted remotely but changed locally."
-msgstr "H σελίδα %s διαγράφτηκε απομακρυσμένα αλλά άλλαξε τοπικά."
+msgstr "Η σελίδα %s διαγράφηκε απομακρυσμένα αλλά άλλαξε τοπικά."
 
 #, python-format
 msgid ""
 "The page %s could not be synced. The remote page was renamed. This is not "
 "supported yet. You may want to delete one of the pages to get it synced."
 msgstr ""
+"Η σελίδα %s δεν κατέστη δυνατόν να συγχρονιστεί. Η απομακρυσμένη σελίδα είχε "
+"μετονομαστεί. Αυτό δεν υποστηρίζεται ακόμα. Ίσως θέλετε να διαγράψετε μία "
+"από τις σελίδες ώστε να γίνει ο συγχρονισμός."
 
 #, python-format
 msgid "Skipped page %s because of a locally or remotely unresolved conflict."
@@ -973,7 +1041,7 @@
 "This is the first synchronisation between the local and the remote wiki for "
 "the page %s."
 msgstr ""
-"Αυτος είναι ο πρώτος συγχρονισμός ανάμεσα στο τοπικό και το απομακρυσμένο "
+"Αυτός είναι ο πρώτος συγχρονισμός ανάμεσα στο τοπικό και το απομακρυσμένο "
 "wiki για τη σελίδα %s."
 
 #, python-format
@@ -991,6 +1059,7 @@
 #, python-format
 msgid "Page %s contains conflicts that were introduced on the remote side."
 msgstr ""
+"Η σελίδα %s περιέχει συγκρούσεις που εισήχθησαν στην απομακρυσμένη έκδοσή της"
 
 #, python-format
 msgid "Page %s merged with conflicts."
@@ -1006,7 +1075,7 @@
 msgstr "Επαναφορά όλων!"
 
 msgid "You are not allowed to use this action."
-msgstr "Δεν επιτρέπεται να χρησιμοποιήσετε αυτη τη λειτουργία."
+msgstr "Δεν επιτρέπεται να χρησιμοποιήσετε αυτή τη λειτουργία."
 
 msgid "Wiki Backup"
 msgstr "Αντίγραφο ασφαλείας του Wiki"
@@ -1029,13 +1098,14 @@
 msgid "Backup"
 msgstr "Αντίγραφο Ασφαλείας"
 
-#, fuzzy
 msgid "You are not allowed to do remote backup."
-msgstr "Δεν επιτρέπεται να επεξεργαστείτε αυτή τη σελίδα."
+msgstr ""
+"Δεν επιτρέπεται να κάνετε λήψη αντιγράφου ασφαλείας σε απομακρυσμένο "
+"μηχάνημα."
 
 #, python-format
 msgid "Unknown backup subaction: %s."
-msgstr ""
+msgstr "Άγνωστη υποενέργεια κατά τη λήψη αντιγράφου ασφαλείας: %s."
 
 msgid "You are not allowed to create the supplementation page."
 msgstr "Δεν επιτρέπεται να δημιουργήσετε επέκταση αυτής της σελίδας!"
@@ -1055,7 +1125,7 @@
 msgstr ""
 "Το όνομα χρήστη {{{'%s'}}} δεν είναι έγκυρο .\n"
 "Το όνομα πρέπει να περιέχει οποιονδήποτε Unicode αλφαριθμητικό χαρακτήρα και "
-"προεραιτικά\n"
+"προαιρετικά\n"
 "ένα κενό ανάμεσα στις λέξεις. Δεν επιτρέπονται τα ονόματα σελίδων group."
 
 msgid "This user name already belongs to somebody else."
@@ -1099,7 +1169,7 @@
 msgstr "TextCha (απαιτείται)"
 
 msgid "Create Profile"
-msgstr "Δημιουργία Προφιλ"
+msgstr "Δημιουργία Προφίλ"
 
 msgid "Create Account"
 msgstr "Δημιουργία Λογαριασμού"
@@ -1115,21 +1185,19 @@
 "revert action. If you want to revert to an older revision, first view that "
 "older revision and then call revert to this (older) revision again."
 msgstr ""
-"Επιλέξατε να γίνετει ανάκληση ενώ βλέπατε την πιο πρόσφατη έκδοση της "
-"σελίδας. Αν θέλετε να ανακαλέσετε σε μια παλιότερη έκδοση, πρώτα δείτε την "
-"παλιότερη και επιλέξτε ξανά ανάκληση σε εκείνη (την παλιά)."
-
-#, fuzzy
+"Επιλέξατε να γίνει ανάκληση ενώ βλέπατε την πιο πρόσφατη έκδοση της σελίδας. "
+"Αν θέλετε να ανακαλέσετε σε μια παλιότερη έκδοση, πρώτα δείτε την παλιότερη "
+"και επιλέξτε ξανά ανάκληση σε εκείνη (την παλιά)."
+
 msgid "Optional reason for reverting this page"
-msgstr "Προεραιτικός λόγος αντιγραφής"
-
-#, fuzzy
+msgstr "Προαιρετικός λόγος επαναφοράς της σελίδας"
+
 msgid "Really revert this page?"
-msgstr "Να διαγραφεί σίγουρα η σελίδα;"
+msgstr "Αλήθεια επαναφορά της σελίδας;"
 
 msgid "Only superuser is allowed to use this action."
 msgstr ""
-"Μόνο οι υπερχρήστες έχουν τη δυνατότητα να εκτελέσουν αυτην την εντολή."
+"Μόνο οι υπερχρήστες έχουν τη δυνατότητα να εκτελέσουν αυτήν την εντολή."
 
 msgid "No page packages found."
 msgstr "Δε βρέθηκαν πακέτα σελίδας"
@@ -1151,9 +1219,8 @@
 msgid "Install language packs for '%s'"
 msgstr "Εγκατάσταση πακέτων γλώσσας για '%s'"
 
-#, fuzzy
 msgid "TextCha: Wrong answer! Try again below..."
-msgstr "TextCha: Λάθος απάντηση! Πηγαίντε πίσω και ξανά-προσπαθήστε..."
+msgstr "TextCha: Λάθος απάντηση! Πηγαίνετε πίσω και ξανά-προσπαθήστε..."
 
 msgid ""
 "Supplying a comment is mandatory.  Write a comment below and try again..."
@@ -1161,20 +1228,22 @@
 
 #, python-format
 msgid "[%d attachments]"
-msgstr ""
+msgstr "[%d επισυναπτόμενα]"
 
 #, python-format
 msgid ""
 "There are <a href=\"%(link)s\">%(count)s attachment(s)</a> stored for this "
 "page."
 msgstr ""
+"Υπάρχουν <a href=\"%(link)s\">%(count)s επισυναπτόμενα</a> αποθηκευμένα γι "
+"αυτή τη σελίδα."
 
 msgid "Filename of attachment not specified!"
-msgstr ""
+msgstr "Το όνομα του αρχείου για το επισυναπτόμενο δεν καθορίστηκε!"
 
 #, python-format
 msgid "Attachment '%(filename)s' does not exist!"
-msgstr ""
+msgstr "Το επισυναπτόμενο '%(filename)s' δεν υπάρχει!"
 
 msgid ""
 "To refer to attachments on a page, use '''{{{attachment:filename}}}''', \n"
@@ -1182,6 +1251,11 @@
 "Do '''NOT''' use the URL of the {{{[get]}}} link, \n"
 "since this is subject to change and can break easily."
 msgstr ""
+"Για να αναφερθείτε σε επισυναπτόμενα σε μία σελίδα, χρησιμοποιήστε "
+"'''{{{attachment:filename}}}''', \n"
+"όπως φαίνεται παρακάτω στη λίστα των αρχείων. \n"
+"ΜΗΝ χρησιμοποιήστε το URL του {{{[get]}}}, \n"
+"καθώς αυτό μπορεί να αλλάξει και να χαλάσει"
 
 msgid "move"
 msgstr "μετακίνηση"
@@ -1191,19 +1265,19 @@
 
 #, python-format
 msgid "No attachments stored for %(pagename)s"
-msgstr ""
+msgstr "Δεν έχουν αποθηκευθεί επισυναπτόμενα για τις %(pagename)s"
 
 msgid "New Attachment"
-msgstr ""
+msgstr "Νέο επισυναπτόμενο"
 
 msgid "File to upload"
 msgstr "Αρχείο προς μεταφόρτωση"
 
 msgid "Rename to"
-msgstr "Μεταονομασία σε"
+msgstr "Μετονομασία σε"
 
 msgid "Overwrite existing attachment of same name"
-msgstr ""
+msgstr "Επικάλυψη υπάρχοντος επισυναπτόμενου με το ίδιο όνομα"
 
 msgid "Upload"
 msgstr "Μεταφόρτωση"
@@ -1212,32 +1286,36 @@
 msgstr "Επισυναπτόμενα Αρχεία"
 
 msgid "You are not allowed to attach a file to this page."
-msgstr "Δεν έχετε το δικαίωμα να επισυνάψετε κάποιο αρχείο σε αυτη τη σελίδα."
+msgstr "Δεν έχετε το δικαίωμα να επισυνάψετε κάποιο αρχείο σε αυτή τη σελίδα."
 
 #, python-format
 msgid "Unsupported AttachFile sub-action: %s"
-msgstr ""
+msgstr "Μη υποστηριζόμενη AttachFile υπό-ενέργεια: %s"
 
 #, python-format
 msgid "Attachments for \"%(pagename)s\""
-msgstr ""
-
-#, fuzzy
+msgstr "Επισυναπτόμενα για \"%(pagename)s\""
+
 msgid "You are not allowed to overwrite a file attachment of this page."
-msgstr "Δεν επιτρέπεται να αλλάξετε το όνομα της σελίδας!"
+msgstr ""
+"Δεν επιτρέπεται να επικαλύψετε επισυναπτόμενα αρχεία αυτής της σελίδας."
 
 #, python-format
 msgid ""
 "Attachment '%(target)s' (remote name '%(filename)s') with %(bytes)d bytes "
 "saved."
 msgstr ""
+"Το επισυναπτόμενο '%(target)s' (απομακρυσμένο όνομα '%(filename)s') με "
+"%(bytes)d bytes αποθηκεύτηκε."
 
 #, python-format
 msgid "Attachment '%(target)s' (remote name '%(filename)s') already exists."
 msgstr ""
+"Το επισυναπτόμενο '%(target)s' (απομακρυσμένο όνομα '%(filename)s') υπάρχει "
+"ήδη."
 
 msgid "You are not allowed to delete attachments on this page."
-msgstr ""
+msgstr "Δεν επιτρέπεται να διαγράψετε τα επισυναπτόμενα αυτής της σελίδας."
 
 #, python-format
 msgid "Attachment '%(filename)s' deleted."
@@ -1246,28 +1324,32 @@
 #, python-format
 msgid "Attachment '%(new_pagename)s/%(new_filename)s' already exists."
 msgstr ""
+"Το επισυναπτόμενο αρχείο '%(new_pagename)s/%(new_filename)s'  υπάρχει ήδη."
 
 #, python-format
 msgid ""
 "Attachment '%(pagename)s/%(filename)s' moved to '%(new_pagename)s/"
 "%(new_filename)s'."
 msgstr ""
+"Το επισυναπτόμενο αρχείο '%(pagename)s/%(filename)s' μετακινήθηκε σε  "
+"'%(new_pagename)s/%(new_filename)s'."
 
 msgid "Nothing changed"
-msgstr "Δεν εφαρμόστηκε καμία αλλαγή"
+msgstr "Δεν έγινε καμία αλλαγή"
 
 #, python-format
 msgid "Page '%(new_pagename)s' does not exist or you don't have enough rights."
 msgstr ""
+"Η σελίδα  '%(new_pagename)s'  δεν υπάρχει ή δεν έχετε αρκετά δικαιώματα."
 
 msgid "Move aborted!"
 msgstr "Η μετακίνηση ακυρώθηκε!"
 
 msgid "You are not allowed to move attachments from this page."
-msgstr ""
+msgstr "Δεν επιτρέπεται να μετακινήσετε επισυναπτόμενα από αυτή τη σελίδα."
 
 msgid "Move aborted because new page name is empty."
-msgstr ""
+msgstr "Η μετακίνηση ακυρώθηκε επειδή το όνομα της νέας σελίδας είναι άδειο."
 
 #, python-format
 msgid "Please use a valid filename for attachment '%(filename)s'."
@@ -1288,13 +1370,13 @@
 msgstr "Νέο όνομα επισύναψης"
 
 msgid "You are not allowed to get attachments from this page."
-msgstr "Δεν έχετε το δικαίωμα να λάβετε επισυνάψεις από αυτη τη σελίδα."
+msgstr "Δεν έχετε το δικαίωμα να λάβετε επισυνάψεις από αυτή τη σελίδα."
 
 msgid "You are not allowed to install files."
 msgstr "Δεν έχετε το δικαίωμα να εγκαταστήσετε αρχεία."
 
 msgid "You are not allowed to unzip attachments of this page."
-msgstr "Δεν επιτρέπεται να αποσυμπιέσετε στοιχεία αυτης της σελίδας."
+msgstr "Δεν επιτρέπεται να αποσυμπιέσετε στοιχεία αυτής της σελίδας."
 
 #, python-format
 msgid "The file %(filename)s is not a .zip file."
@@ -1347,12 +1429,12 @@
 
 msgid "Unknown file type, cannot display this attachment inline."
 msgstr ""
-"Λανθασμένος τύπος αρχείου, αδυναμία προβολής αυτου του επισυναπτόμενου "
+"Λανθασμένος τύπος αρχείου, αδυναμία προβολής αυτού του επισυναπτόμενου "
 "αρχείου."
 
 #, python-format
 msgid "attachment:%(filename)s of %(pagename)s"
-msgstr "επισυναπτόμρνο αρχείο:%(filename)s του %(pagename)s"
+msgstr "επισυναπτόμενο αρχείο:%(filename)s του %(pagename)s"
 
 msgid "Load"
 msgstr "Φόρτωση"
@@ -1439,11 +1521,11 @@
 msgstr ""
 
 msgid "OpenID Trust verification"
-msgstr ""
+msgstr "Επιβεβαίωση εμπιστοσύνης OpenID"
 
 #, python-format
 msgid "The site %s has asked for your identity."
-msgstr ""
+msgstr "Ο ιστοτόπος %s ζήτησε την ταυτότητά σας."
 
 #, python-format
 msgid ""
@@ -1458,13 +1540,13 @@
 msgstr ""
 
 msgid "Identity URL"
-msgstr ""
+msgstr "URL ταυτότητας"
 
 msgid "Remember decision"
 msgstr "Απομνημόνευση επιλογής"
 
 msgid "Remember this trust decision and don't ask again"
-msgstr ""
+msgstr "Απομνημόνευση αυτής της επιλογής (δε θα ερωτηθείτε ξανά)"
 
 msgid "Approve"
 msgstr "Αποδοχή"
@@ -1518,7 +1600,6 @@
 msgid "Available attachments for page"
 msgstr "Διαθέσιμα επισυναπτόμενα αρχεία για τη σελίδα"
 
-#, fuzzy
 msgid "If this account exists an email was sent."
 msgstr "Αν όντως υπάρχει τέτοιος λογαριασμός, το email στάλθηκε."
 
@@ -1529,9 +1610,8 @@
 "Δεν έχει ενεργοποιηθεί η δυνατότητα επεξεργασίας mail γι' αυτό το wiki.\n"
 "Επικοινωνήστε με τον ιδιοκτήτη του. Αυτός μπορεί να ενεργοποιήσει το email."
 
-#, fuzzy
 msgid "Please provide a valid email address or a username!"
-msgstr "Παρακαλώ βάλτε μια έγκυρη διεύθυνση email!"
+msgstr "Παρακαλώ βάλτε μια έγκυρη διεύθυνση email η όνομα χρήστη!"
 
 msgid "Username"
 msgstr "Όνομα Χρήστη"
@@ -1540,7 +1620,7 @@
 msgstr "Στείλτε μου τα δεδομένα του λογαριασμού με email"
 
 msgid "Recovery token"
-msgstr ""
+msgstr "Token ανάκτησης"
 
 msgid "New password"
 msgstr "Νέος Κωδικός"
@@ -1548,25 +1628,26 @@
 msgid "New password (repeat)"
 msgstr "Νέος κωδικός (επανάληψη)"
 
-#, fuzzy
 msgid "Reset my password"
-msgstr "Κωδικός"
+msgstr "Επαναφορά του κωδικού μου"
 
 msgid "Your password has been changed, you can log in now."
 msgstr "Ο κωδικός σας έχει αλλάξει, μπορείτε πλέον να συνδεθείτε."
 
 msgid "Your token is invalid!"
-msgstr ""
-
-#, fuzzy
+msgstr "Το token σας δεν είναι έγκυρο!"
+
 msgid "Password reset"
-msgstr "Κωδικός"
+msgstr "Επαναφορά κωδικού"
 
 msgid ""
 "\n"
 "== Password reset ==\n"
 "Enter a new password below."
 msgstr ""
+"\n"
+"== Επαναφορά κωδικού ==\n"
+"Εισάγετε ένα νέο κωδικό παρακάτω."
 
 msgid "Lost password"
 msgstr "Χαμένος Κωδικός"
@@ -1623,8 +1704,8 @@
 #, python-format
 msgid "Please use a more selective search term instead of {{{\"%s\"}}}"
 msgstr ""
-"Παρακαλώ χρησιμοποιείστε κάποιον πιο συγκεκριμένο όρο αναζήτης αντί για τον "
-"{{{\"%s\"}}}"
+"Παρακαλώ χρησιμοποιείστε κάποιον πιο συγκεκριμένο όρο αναζήτησης αντί για "
+"τον {{{\"%s\"}}}"
 
 #, python-format
 msgid "Title Search: \"%s\""
@@ -1643,12 +1724,17 @@
 "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 ""
+"Η αναζήτηση για {{{\"%s\"}}} δεν είχε αποτελέσματα. Παρακαλούμε αλλάξτε "
+"κάποιους από τους όρους και ανατρέξτε στο HelpOnSearching για περισσότερες "
+"πληροφορίες.%s"
 
 msgid "(!) Consider performing a"
 msgstr "(!) Θυμηθείτε να κάνετε μια"
@@ -1661,7 +1747,7 @@
 "results of your search query in this wiki. <<BR>>"
 msgstr ""
 "(!) Εκτελείτε μια αναζήτηση τίτλου που μπορεί να μην περιλαμβάνει όλα τα "
-"διαθέσιμα αποτελέσματα τις αναζήτησης σε αυτο το wiki. <<BR>>"
+"διαθέσιμα αποτελέσματα τις αναζήτησης σε αυτό το wiki. <<BR>>"
 
 msgid "Click here to perform a full-text search with your search terms!"
 msgstr ""
@@ -1669,11 +1755,10 @@
 "όρους αναζήτησης σας!"
 
 msgid "Rename Page"
-msgstr "Μεταονομασία Σελίδας"
+msgstr "Μετονομασία Σελίδας"
 
 msgid "Create redirect for renamed page(s)?"
-msgstr ""
-"Να δημιουργηθούν ανακατευθύνσεις για την σελίδα(ες) που μετονομάστηκαν;"
+msgstr "Να δημιουργηθούν ανακατευθύνσεις για τις σελίδες που μετονομάστηκαν;"
 
 msgid "Rename all /subpages too?"
 msgstr "Να μετονομαστούν όλες οι /υποσελίδες επίσης;"
@@ -1682,10 +1767,10 @@
 msgstr "Να δημιουργηθεί ανακατεύθυνση για την σελίδα που μετονομάστηκε;"
 
 msgid "Really rename this page?"
-msgstr "Θέλετε όντως να μεταονομάσετε αυτη τη σελίδα;"
+msgstr "Θέλετε όντως να μετονομάσετε αυτή τη σελίδα;"
 
 msgid "Optional reason for the renaming"
-msgstr "Προεραιτικός λόγος για τη μετονομασία"
+msgstr "Προαιρετικός λόγος για τη μετονομασία"
 
 msgid "You must login to remove a quicklink."
 msgstr ""
@@ -1704,11 +1789,8 @@
 "Δεν έχετε το δικαίωμα να κάνετε συνδρομή σε μια σελίδα που δεν μπορείτε να "
 "διαβάσετε."
 
-#, fuzzy
 msgid "This wiki is not enabled for mail/Jabber processing."
-msgstr ""
-"Δεν έχει ενεργοποιηθεί η δυνατότητα επεξεργασίας mail γι' αυτό το wiki.\n"
-"Επικοινωνήστε με τον ιδιοκτήτη του. Αυτός μπορεί να ενεργοποιήσει το email."
+msgstr "Αυτό το  wiki δεν είναι διαθέσιμο για mail/jabber επεξεργασία."
 
 msgid "You must log in to use subscriptions."
 msgstr "Πρέπει να συνδεθείτε για να χρησιμοποιήσετε τις συνδρομές"
@@ -1721,13 +1803,13 @@
 "ρυθμίσεις χρήστη για να χρησιμοποιήσετε τις συνδρομές."
 
 msgid "You are already subscribed to this page."
-msgstr "Έχετε κάνει ήδη συνδρομή σε αυτη τη σελίδα."
+msgstr "Έχετε κάνει ήδη συνδρομή σε αυτή τη σελίδα."
 
 msgid "You have been subscribed to this page."
-msgstr "Έχετε κάνει συνδρομή σε αυτη τη σελίδα."
+msgstr "Έχετε κάνει συνδρομή σε αυτή τη σελίδα."
 
 msgid "You could not get subscribed to this page."
-msgstr "Δεν μπορέσατε να κάνετε συνδρομή σε αυτη τη σελίδα."
+msgstr "Δεν μπορέσατε να κάνετε συνδρομή σε αυτή τη σελίδα."
 
 #, python-format
 msgid "Exactly one page like \"%s\" found, redirecting to page."
@@ -1752,7 +1834,7 @@
 msgstr "Συνδρομή χρηστών στη σελίδα %s"
 
 msgid "Enter user names (comma separated):"
-msgstr "Πληκτρολογήστε τα ονόματα χρηστών (χωρίστε τα με κομμα)"
+msgstr "Πληκτρολογήστε τα ονόματα χρηστών (χωρίστε τα με κόμμα)"
 
 #, python-format
 msgid "Subscribed for %s:"
@@ -1762,10 +1844,10 @@
 msgstr "Δεν υπάρχει ο χρήστης:"
 
 msgid "You are not allowed to perform this action."
-msgstr "Δεν έχετε το δικαίωμα να εκτελέσετε αυτη την ενέργεια."
+msgstr "Δεν έχετε το δικαίωμα να εκτελέσετε αυτή την ενέργεια."
 
 msgid "HelpOnParsers"
-msgstr ""
+msgstr "HelpOnParsers"
 
 #, python-format
 msgid ""
@@ -1817,9 +1899,9 @@
 msgid "**Maximum number of allowed includes exceeded**"
 msgstr ""
 
-#, fuzzy, python-format
+#, python-format
 msgid "**You are not allowed to read the page: %s**"
-msgstr "Δεν επιτρέπεται να αλλάξετε το όνομα της σελίδας!"
+msgstr "**Δεν επιτρέπεται η ανάγνωση της σελίδας: %s**"
 
 #, python-format
 msgid "**Could not find the referenced page: %s**"
@@ -1859,7 +1941,7 @@
 
 #, python-format
 msgid "Expected an integer \"%(key)s\" before \"%(token)s\""
-msgstr "Προβλεπόμενος ακέραιος αριθμός \"%(key)s\" πρίν από \"%(token)s\""
+msgstr "Προβλεπόμενος ακέραιος αριθμός \"%(key)s\" πριν από \"%(token)s\""
 
 #, python-format
 msgid "Expected an integer \"%(arg)s\" after \"%(key)s\""
@@ -1873,16 +1955,16 @@
 msgstr "Η σελίδα έχει αλλάξει"
 
 msgid "Page has been modified in a trivial fashion"
-msgstr "Η σελίδα έχει αλλάξει με ελάχιστες αλλαγές"
+msgstr "Η σελίδα έχει αλλάξει με τετριμμένο τρόπο"
 
 msgid "Page has been renamed"
-msgstr "Η σελίδα έχει μεταονομαστεί"
+msgstr "Η σελίδα έχει μετονομαστεί"
 
 msgid "Page has been deleted"
-msgstr "Η σελίδα έχει διαγραφτεί"
+msgstr "Η σελίδα έχει διαγραφεί"
 
 msgid "Page has been copied"
-msgstr "Η σελίδα έχει αντιγραφτεί"
+msgstr "Η σελίδα έχει αντιγραφεί"
 
 msgid "A new attachment has been added"
 msgstr "Ένα νέο επισυναπτόμενο αρχείο έχει προστεθεί"
@@ -1894,7 +1976,7 @@
 msgstr "Μια σελίδα έχει επιστρέψει σε προηγούμενη κατάσταση"
 
 msgid "A user has subscribed to a page"
-msgstr "Ένας χρήστης έκανε συνδρομή σε αυτη τη σελίδα"
+msgstr "Ένας χρήστης έκανε συνδρομή σε αυτή τη σελίδα"
 
 msgid "A new account has been created"
 msgstr "Ένας νέος λογαριασμός δημιουργήθηκε"
@@ -1904,13 +1986,15 @@
 msgstr "[%(sitename)s] %(trivial)sUpdate του \"%(pagename)s\" από %(username)s"
 
 msgid "Trivial "
-msgstr "Ελάχιστος"
+msgstr "Τετριμμένο "
 
 #, python-format
 msgid ""
 "Attachment link: %(attach)s\n"
 "Page link: %(page)s\n"
 msgstr ""
+"Σύνδεσμος επισύναψης: %(attach)s\n"
+"Σύνδεσμος σελίδας: %(page)s\n"
 
 msgid "Attachment link"
 msgstr "Σύνδεσμος επισυναπτόμενου αρχείου"
@@ -2004,7 +2088,7 @@
 msgstr ""
 "[%(sitename)s] Νέο επισυναπτόμενο αρχείο προστέθηκε στη σελίδα %(pagename)s"
 
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "Dear Wiki user,\n"
 "\n"
@@ -2017,13 +2101,11 @@
 msgstr ""
 "Αγαπητή/ε χρήστη του Wiki,\n"
 "\n"
-"Έχετε εγγραφεί έτσι ώστε να ενημερώνεστε για μια σελίδα ή κατηγορία wiki με "
-"όνομα \"%(sitename)s\".\n"
+"Έχετε εγγραφεί συνδρομητής σε μία σελίδα wiki \"%(page_name)s\" έτσι ώστε να "
+"ενημερώνεστε για τυχόν αλλαγές.  Μία επισύναψη έχει προστεθεί στη σελίδα από "
+"τον/την %(editor)s. Ακολουθούν λεπτομερείς πληροφορίες:\n"
 "\n"
-"Η σελίδα \"%(pagename)s\" απέκτησε ένα νέο επισυναπτόμενο αρχείο από τον "
-"%(editor)s. Είναι διαθέσιμες επιπρόσθετες πληροφορίες:\n"
-"\n"
-"Όνομασία επισυναπτόμενου αρχείου: %(attach_name)s\n"
+"Ονομασία επισυναπτόμενου αρχείου: %(attach_name)s\n"
 "Μέγεθος επισυναπτόμενου αρχείου: %(attach_size)s\n"
 
 #, python-format
@@ -2031,7 +2113,7 @@
 msgstr ""
 "[%(sitename)s] Αφαίρεση επισυναπτόμενων αρχείων από την σελίδα %(pagename)s"
 
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "Dear Wiki user,\n"
 "\n"
@@ -2044,29 +2126,29 @@
 msgstr ""
 "Αγαπητή/ε χρήστη του Wiki,\n"
 "\n"
-"Έχετε εγγραφεί έτσι ώστε να ενημερώνεστε για μια σελίδα ή κατηγορία wiki με "
-"όνομα \"%(sitename)s\".\n"
+"Έχετε εγγραφεί συνδρομητής σε μία σελίδα wiki \"%(page_name)s\" έτσι ώστε να "
+"ενημερώνεστε για τυχόν αλλαγές.  Μία επισύναψη έχει αφαιρεθεί από τη σελίδα "
+"από τον/την %(editor)s. Ακολουθούν λεπτομερείς πληροφορίες:\n"
 "\n"
-"Από την σελίδα \"%(pagename)s\" αφαιρέθηκε ένα επισυναπτόμενο αρχείο από τον "
-"%(editor)s. Είναι διαθέσιμες επιπρόσθετες πληροφορίες:\n"
-"\n"
-"Όνομασία επισυναπτόμενου αρχείου: %(attach_name)s\n"
+"Ονομασία επισυναπτόμενου αρχείου: %(attach_name)s\n"
 "Μέγεθος επισυναπτόμενου αρχείου: %(attach_size)s\n"
 
 msgid "Options --pages and --search are mutually exclusive!"
-msgstr ""
+msgstr "Οι επιλογές --pages και --search είναι αμοιβαίως αποκλειόμενες"
 
 msgid "You must specify an output file!"
 msgstr "Πρέπει να θέσετε κάποιο αρχείο εξόδου!"
 
 msgid "No pages specified using --pages or --search, assuming full package."
 msgstr ""
+"Δεν καθορίστηκαν σελίδες με χρήση των --pages ή --search, θεωρώ ότι εννοείτε "
+"το πλήρες πακέτο."
 
 msgid "All attachments included into the package."
 msgstr "Όλα τα επισυναπτόμενα αρχεία περιλαμβάνονται μέσα στο πακέτο."
 
 msgid "Output file already exists! Cowardly refusing to continue!"
-msgstr "Το αρχείο εξόδου υπάρχει ήδη! Φοβάμαι να συνέχισω..."
+msgstr "Το αρχείο εξόδου υπάρχει ήδη! Φοβάμαι να συνεχίσω..."
 
 msgid "Missing password. Please enter user name and password."
 msgstr ""
@@ -2102,7 +2184,7 @@
 "OpenID σας."
 
 msgid "Choose this name"
-msgstr "Επιλέξτε αυτο το όνομα"
+msgstr "Επιλέξτε αυτό το όνομα"
 
 msgid "This is not a valid username, choose a different one."
 msgstr ""
@@ -2114,7 +2196,7 @@
 "the username with your OpenID. Otherwise, please choose a different\n"
 "username and leave the password field blank."
 msgstr ""
-"To όνομα χρήστη που επιλέξατε είναι ήδη\n"
+"Το όνομα χρήστη που επιλέξατε είναι ήδη\n"
 "χρησιμοποιημένο. Εάν είναι αυτό το δικό σας, γράψτε από κάτω τον κωδικό για "
 "να συσχετίσετε\n"
 "το όνομα χρήστη με το OpenID σας. Διαφορετικά, παρακαλώ επιλέξτε ένα "
@@ -2122,7 +2204,7 @@
 "όνομα και αφήστε το κενό με τον κωδικό."
 
 msgid "Associate this name"
-msgstr "Συσχέτιση αυτου του ονόματος"
+msgstr "Συσχέτιση αυτού του ονόματος"
 
 #, python-format
 msgid "OpenID error: %s."
@@ -2145,7 +2227,7 @@
 msgstr "Αποτυχία OpenID."
 
 msgid "No OpenID found in session."
-msgstr "Δεν βρέθηκε κάποιο OpenID κατα τη διάρκεια της συνεδρίας."
+msgstr "Δεν βρέθηκε κάποιο OpenID κατά τη διάρκεια της συνεδρίας."
 
 msgid "Your account is now associated to your OpenID."
 msgstr "Ο λογαριασμός σας τώρα συνδέεται με το OpenID σας."
@@ -2177,7 +2259,7 @@
 "create one during login."
 msgstr ""
 "Εάν δεν έχετε κάποιο λογαριασμό ακόμα, μπορείτε να συνδεθείτε με το OpenID "
-"σας και να δημιουργήσετε ένα κατα τη διάρκεια της σύνδεσης."
+"σας και να δημιουργήσετε ένα κατά τη διάρκεια της σύνδεσης."
 
 #, python-format
 msgid "LDAP server %(server)s failed."
@@ -2202,7 +2284,7 @@
 msgstr "Διαγραφή"
 
 msgid "Subscribe"
-msgstr "Εγγραφή"
+msgstr "Συνδρομή"
 
 msgid "Raw"
 msgstr "Απλό"
@@ -2276,7 +2358,7 @@
 msgstr "Αναζήτηση"
 
 msgid "More Actions:"
-msgstr "Περισότερες επιλογές:"
+msgstr "Περισσότερες επιλογές:"
 
 msgid "------------------------"
 msgstr "------------------------"
@@ -2285,7 +2367,7 @@
 msgstr "Απλό Κείμενο"
 
 msgid "Print View"
-msgstr ""
+msgstr "Προεπισκόπηση εκτύπωσης"
 
 msgid "Delete Cache"
 msgstr "Διαγραφή Προσωρινής Μνήμης"
@@ -2306,13 +2388,13 @@
 msgstr "Συνδρομή στο χρήστη "
 
 msgid "Remove Spam"
-msgstr "Διαγραφή Ανεπυθίμιτων Μυνημάτων"
+msgstr "Διαγραφή Ανεπιθύμητων Μηνυμάτων"
 
 msgid "Package Pages"
 msgstr "Σελίδες Πακέτων"
 
 msgid "Render as Docbook"
-msgstr ""
+msgstr "Αποτύπωση ως DocBook"
 
 msgid "Sync Pages"
 msgstr "Συγχρονισμός Σελίδων"
@@ -2349,14 +2431,14 @@
 msgstr ""
 
 msgid "DeleteCache"
-msgstr "ΔιαγραφήΠροσωρινήςΜνήμης"
+msgstr ""
 
 #, python-format
 msgid "(cached %s)"
 msgstr "(προσωρινά αποθηκευμένο %s)"
 
 msgid "Or try one of these actions:"
-msgstr "Η δοκιμάστε μια από τις ακόλουθες ενέργειες:"
+msgstr "Ή δοκιμάστε μια από τις ακόλουθες ενέργειες:"
 
 msgid "Wiki"
 msgstr "Wiki"
@@ -2374,17 +2456,17 @@
 msgstr "Ο κωδικός είναι πολύ σύντομος."
 
 msgid "Password has not enough different characters."
-msgstr "Ο κωδικός δεν έχει αρκετά διαφορετικούς χαρακηρες."
+msgstr "Ο κωδικός δεν έχει αρκετούς διαφορετικούς χαρακτήρες."
 
 msgid ""
 "Password is too easy (password contains name or name contains password)."
 msgstr "Ο κωδικός είναι πολύ εύκολος (ο κωδικός περιέχει κάποια ονομασία)"
 
 msgid "Password is too easy (keyboard sequence)."
-msgstr "Ο κωδικός είναι πολύ εύκολος (συστοιχία πλήκτρων)."
+msgstr "Ο κωδικός είναι πολύ εύκολος (ακολουθία πλήκτρων)."
 
 msgid "UnSubscribe"
-msgstr "Διακοπή Συνδρομής"
+msgstr ""
 
 msgid "Publish my email (not my wiki homepage) in author info"
 msgstr ""
@@ -2402,10 +2484,10 @@
 msgstr "Εμφάνιση περιοχών σχολίων"
 
 msgid "Show question mark for non-existing pagelinks"
-msgstr ""
+msgstr "Εμφάνιση ερωτηματικού για μη-υπάρχοντες συνδέσμους σε σελίδες"
 
 msgid "Show page trail"
-msgstr ""
+msgstr "Εμφάνιση ίχνους σελίδας"
 
 msgid "Show icon toolbar"
 msgstr "Εμφάνιση εικονιδίου εργαλειοθήκης"
@@ -2423,7 +2505,7 @@
 msgstr "Απομνημόνευση των πληροφοριών σύνδεσης"
 
 msgid "Disable this account forever"
-msgstr "Απενεργοποίηση αυτου του λογαριασμού για πάντα"
+msgstr "Απενεργοποίηση αυτού του λογαριασμού για πάντα"
 
 msgid "Alias-Name"
 msgstr "Ψευδώνυμο"
@@ -2449,10 +2531,10 @@
 
 #, python-format
 msgid "Inlined image: %(url)s"
-msgstr ""
+msgstr "Εμβόλιμη εικόνα: %(url)s"
 
 msgid "Toggle line numbers"
-msgstr ""
+msgstr "(Απ)ενεργοποίηση αρίθμησης γραμμών"
 
 msgid "From"
 msgstr "Από"
@@ -2469,7 +2551,7 @@
 #, python-format
 msgid "Connection to mailserver '%(server)s' failed: %(reason)s"
 msgstr ""
-"Η σύνδεση στο διακομιστή ηλεκτρονικού ταχυδρομίου '%(server)s' απέτυχε: "
+"Η σύνδεση στο διακομιστή ηλεκτρονικού ταχυδρομείου '%(server)s' απέτυχε: "
 "%(reason)s"
 
 msgid "Mail not sent"
@@ -2479,7 +2561,7 @@
 msgstr "Το mail στάλθηκε επιτυχώς"
 
 msgid "RecentChanges"
-msgstr "ΠρόσφατεςΑλλαγές"
+msgstr ""
 
 msgid "WikiTipOfTheDay"
 msgstr ""
@@ -2491,7 +2573,7 @@
 msgstr ""
 
 msgid "FindPage"
-msgstr "ΕύρεσηΣελίδας"
+msgstr ""
 
 msgid "MissingPage"
 msgstr ""
@@ -2502,9 +2584,8 @@
 msgid "WikiHomePage"
 msgstr ""
 
-#, fuzzy
 msgid "WikiName"
-msgstr "Όνομα"
+msgstr ""
 
 msgid "WikiWikiWeb"
 msgstr ""
@@ -2515,20 +2596,17 @@
 msgid "WikiSandBox"
 msgstr ""
 
-#, fuzzy
 msgid "InterWiki"
-msgstr "Wiki"
+msgstr "InterWiki"
 
 msgid "AbandonedPages"
 msgstr ""
 
-#, fuzzy
 msgid "OrphanedPages"
-msgstr "Σελίδες πακέτου"
-
-#, fuzzy
+msgstr ""
+
 msgid "WantedPages"
-msgstr "Σελίδες πακέτου"
+msgstr ""
 
 msgid "EventStats"
 msgstr ""
@@ -2542,26 +2620,23 @@
 msgid "EventStats/UserAgents"
 msgstr ""
 
-#, fuzzy
 msgid "PageSize"
-msgstr "Σελίδα"
-
-#, fuzzy
+msgstr ""
+
 msgid "PageHits"
-msgstr "Σελίδα"
+msgstr ""
 
 msgid "RandomPage"
-msgstr "ΤυχαίαΣελίδα"
-
-#, fuzzy
+msgstr ""
+
 msgid "XsltVersion"
-msgstr "Έκδοση Python"
+msgstr ""
 
 msgid "FortuneCookies"
 msgstr ""
 
 msgid "WikiLicense"
-msgstr "ΆδειαWiki"
+msgstr ""
 
 msgid "CategoryCategory"
 msgstr ""
@@ -2576,7 +2651,7 @@
 msgstr ""
 
 msgid "HelpTemplate"
-msgstr "ΒοηθητικόΠρότυπο"
+msgstr ""
 
 msgid "HomepageReadWritePageTemplate"
 msgstr ""
@@ -2593,9 +2668,8 @@
 msgid "SlideShowHandOutTemplate"
 msgstr ""
 
-#, fuzzy
 msgid "SlideShowTemplate"
-msgstr "Slideshow"
+msgstr ""
 
 msgid "SlideTemplate"
 msgstr ""
@@ -2648,9 +2722,8 @@
 msgid "HelpOnNotification"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnRobots"
-msgstr "Σχόλιο:"
+msgstr ""
 
 msgid "HelpOnSessions"
 msgstr ""
@@ -2674,7 +2747,7 @@
 msgstr "MoinMoin"
 
 msgid "HelpContents"
-msgstr "ΒοηθητικόΠεριεχόμενο"
+msgstr ""
 
 msgid "HelpForBeginners"
 msgstr ""
@@ -2688,9 +2761,8 @@
 msgid "HelpOnAccessControlLists"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnActions"
-msgstr "Δράση"
+msgstr ""
 
 msgid "HelpOnActions/AttachFile"
 msgstr ""
@@ -2707,9 +2779,8 @@
 msgid "HelpOnDictionaries"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnDrawings"
-msgstr "Γλώσσα"
+msgstr ""
 
 msgid "HelpOnEditLocks"
 msgstr ""
@@ -2723,20 +2794,17 @@
 msgid "HelpOnGraphicalEditor"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnGroups"
-msgstr "Δράση"
+msgstr ""
 
 msgid "HelpOnHeadlines"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnImages"
-msgstr "Γλώσσα"
-
-#, fuzzy
+msgstr ""
+
 msgid "HelpOnLanguages"
-msgstr "Γλώσσα"
+msgstr ""
 
 msgid "HelpOnLinking"
 msgstr ""
@@ -2747,9 +2815,8 @@
 msgid "HelpOnLists"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnLogin"
-msgstr "Είσοδος"
+msgstr ""
 
 msgid "HelpOnMacros"
 msgstr ""
@@ -2793,9 +2860,8 @@
 msgid "HelpOnSearching"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnSlideShows"
-msgstr "Slideshow"
+msgstr ""
 
 msgid "HelpOnSlideShows/000 Introduction"
 msgstr ""
@@ -2830,9 +2896,8 @@
 msgid "HelpOnThemes"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnUserPreferences"
-msgstr "Επιλογές"
+msgstr ""
 
 msgid "HelpOnVariables"
 msgstr ""
@@ -2840,13 +2905,11 @@
 msgid "HelpOnXmlPages"
 msgstr ""
 
-#, fuzzy
 msgid "HelpOnComments"
-msgstr "Σχόλιο:"
-
-#, fuzzy
+msgstr ""
+
 msgid "HelpOnSubscribing"
-msgstr "Εγγραφή"
+msgstr ""
 
 msgid "CamelCase"
 msgstr ""
@@ -2866,9 +2929,8 @@
 msgid "WikiCourse/04 Creating a wiki account"
 msgstr ""
 
-#, fuzzy
 msgid "WikiCourse/05 User preferences"
-msgstr "Οι ρυθμίσεις χρήστη αποθηκεύτηκαν!"
+msgstr ""
 
 msgid "WikiCourse/06 Your own wiki homepage"
 msgstr ""
@@ -2903,9 +2965,8 @@
 msgid "WikiCourse/17 External links"
 msgstr ""
 
-#, fuzzy
 msgid "WikiCourse/18 Attachments"
-msgstr "Επισυναπτόμενα αρχεία"
+msgstr ""
 
 msgid "WikiCourse/19 Symbols"
 msgstr ""
@@ -2919,9 +2980,8 @@
 msgid "WikiCourse/22 Parsers"
 msgstr ""
 
-#, fuzzy
 msgid "WikiCourse/23 Actions"
-msgstr "Περισότερες επιλογές:"
+msgstr ""
 
 msgid "WikiCourse/30 The graphical editor"
 msgstr ""
@@ -2972,13 +3032,13 @@
 msgstr "ΑγαπημένεςΣελίδες"
 
 msgid "LocalSiteMap"
-msgstr ""
+msgstr "LocalSiteMap"
 
 msgid "RenamePage"
-msgstr "ΜεταονομασίαΣελίδας"
+msgstr "RenamePage"
 
 msgid "SpellCheck"
-msgstr "ΟρθογραφικόςΈλεγχος"
+msgstr "SpellCheck"
 
 msgid "Discussion"
 msgstr "Συζήτηση"
@@ -2987,7 +3047,7 @@
 msgstr "Εναλλαγή χρήστη"
 
 msgid "No user selected"
-msgstr "Δεν επιλέχτηκε κάποιος χρήστης"
+msgstr "Δεν επιλέχθηκε κάποιος χρήστης"
 
 msgid ""
 "You can now change the settings of the selected user account; log out to get "
@@ -3009,10 +3069,10 @@
 msgstr "Επιλογή Χρήστη"
 
 msgid "Notification"
-msgstr "Υπενθίμιση"
+msgstr "Υπενθύμιση"
 
 msgid "Notification settings saved!"
-msgstr "Οι ρυθμίσεις των υπενθιμίσεων αποθηκεύτηκαν."
+msgstr "Οι ρυθμίσεις των υπενθυμίσεων αποθηκεύτηκαν."
 
 msgid "'''Email'''"
 msgstr "'''Email'''"
@@ -3030,15 +3090,14 @@
 "Before you can be notified, you need to provide a way to contact you in the "
 "general preferences."
 msgstr ""
-"Πρωτού σας σταλούν ειδοποιήσεις, θα πρέπει να παρέχετε ένα τρόπο "
+"Προτού σας σταλούν ειδοποιήσεις, θα πρέπει να παρέχετε ένα τρόπο "
 "επικοινωνίας στις γενικές προτιμήσεις."
 
 msgid "Subscribed events"
 msgstr "Γεγονότα στα οποία έγινε συνδρομή"
 
-#, fuzzy
 msgid "Subscribed wiki pages<<BR>>(one regex per line)"
-msgstr "Σελίδες wiki που έχετε γραφτεί (μόνο ένα regex ανά γραμμή)"
+msgstr "Σελίδες wiki όπου έχετε συνδρομή (μόνο ένα regex ανά γραμμή)"
 
 msgid "Save"
 msgstr "Αποθήκευση"
@@ -3084,13 +3143,13 @@
 msgstr "<Επιλογές Φυλλομετρητή>"
 
 msgid "the one preferred"
-msgstr "το προτιμόμενο"
+msgstr "το προτιμώμενο"
 
 msgid "free choice"
 msgstr "ελεύθερη επιλογή"
 
 msgid "Preferred theme"
-msgstr "Προτιμόμενο θέμα"
+msgstr "Προτιμώμενο θέμα"
 
 msgid "Editor Preference"
 msgstr "Επιλογές του editor"
@@ -3111,7 +3170,7 @@
 msgstr "Μορφή ημερομηνίας"
 
 msgid "Preferred language"
-msgstr "Προτιμόμενη γλώσσα"
+msgstr "Προτιμώμενη γλώσσα"
 
 msgid "General options"
 msgstr "Γενικές επιλογές"
@@ -3123,19 +3182,19 @@
 msgstr "Ρυθμίσεις OpenID"
 
 msgid "Cannot remove all OpenIDs."
-msgstr "Αδυναμία διαφραφής όλων των OpenIDs."
+msgstr "Αδυναμία διαγραφής όλων των OpenIDs."
 
 msgid "The selected OpenIDs have been removed."
 msgstr "Τα επιλεγμένα OpenIDs έχουν διαγραφεί."
 
 msgid "No OpenID given."
-msgstr "Δεν δώθηκε κάποιο OpenID"
+msgstr "Δεν δόθηκε κάποιο OpenID"
 
 msgid "OpenID is already present."
 msgstr "Το OpenID υπάρχει ήδη."
 
 msgid "This OpenID is already used for another account."
-msgstr "Αυτο το OpenID βρίσκεται ήδη σε χρήση από κάποιον άλλο λογαριασμό."
+msgstr "Αυτό το OpenID βρίσκεται ήδη σε χρήση από κάποιον άλλο λογαριασμό."
 
 msgid "OpenID added successfully."
 msgstr "Το OpenID προστέθηκε με επιτυχία."
@@ -3176,19 +3235,19 @@
 msgstr "Άλλα"
 
 msgid "User agent"
-msgstr ""
+msgstr "User Agent"
 
 msgid "Distribution of User-Agent Types"
-msgstr ""
+msgstr "Κατανομή τύπων User-Agent"
 
 msgid "Page Size Distribution"
-msgstr "Διανομή μεγέθους σελίδας"
+msgstr "Κατανομή μεγέθους σελίδας"
 
 msgid "page size upper bound [bytes]"
-msgstr ""
+msgstr "άνω όριο μεγέθους σελίδας [bytes]"
 
 msgid "# of pages of this size"
-msgstr "# από σελίδες αυτου του μεγέθους"
+msgstr "# από σελίδες αυτού του μεγέθους"
 
 msgid "[all]"
 msgstr "[όλα]"
@@ -3200,7 +3259,7 @@
 msgstr "[κενό]"
 
 msgid "filter"
-msgstr ""
+msgstr "φίλτρο"
 
 msgid "OpenID"
 msgstr "OpenID"
@@ -3228,7 +3287,7 @@
 "Sorry, can not save page because \"%(content)s\" is not allowed in this wiki."
 msgstr ""
 "Λυπούμαστε, δεν μπορείτε να αποθηκεύσετε την σελίδα καθώς το \"%(content)s\" "
-"δεν επιτρέπεται σε αυτο το wiki."
+"δεν επιτρέπεται σε αυτό το wiki."
 
 #, python-format
 msgid "<<%(macro_name)s: execution failed [%(error_msg)s] (see also the log)>>"
@@ -3248,21 +3307,27 @@
 "%(extension_name)s %(extension_type)s: Required argument %(argument_name)s "
 "missing."
 msgstr ""
+"%(extension_name)s %(extension_type)s: Απαιτούμενο όρισμα %(argument_name)s "
+"λείπει."
 
 #, 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!"
 
 #, python-format
 msgid ""
 "Current configuration does not allow embedding of the file %(file)s because "
 "of its mimetype %(mimetype)s."
 msgstr ""
+"Η τωρινή παραμετροποίηση δεν επιτρέπει την ενσωμάτωση του αρχείου %(file)s "
+"λόγω του mimetype του %(mimetype)s."
 
 msgid "Embedded"
-msgstr "Περιεχόμενος"
+msgstr "Ενσωματωμένο"
 
 #, python-format
 msgid "Invalid include arguments \"%s\"!"
@@ -3315,7 +3380,7 @@
 msgstr "Ρύθμιση"
 
 msgid "No orphaned pages in this wiki."
-msgstr "Δεν υπάρχουν ορφανές σελίδες σε αυτο το wiki."
+msgstr "Δεν υπάρχουν ορφανές σελίδες σε αυτό το wiki."
 
 msgid "File attachment browser"
 msgstr "Εξερευνητής επισυναπτόμενων αρχείων"
@@ -3336,9 +3401,9 @@
 msgid "Search Text"
 msgstr "Αναζήτηση Κειμένου"
 
-#, fuzzy, python-format
+#, python-format
 msgid "Unknown macro parameter: %s."
-msgstr "Άγνωστη εντολή %(action_name)s."
+msgstr "Άγνωστη παράμετρος μακροεντολής: %s."
 
 #, python-format
 msgid ""
@@ -3349,13 +3414,11 @@
 msgid "Lexer description"
 msgstr ""
 
-#, fuzzy
 msgid "Lexer names"
-msgstr "Χρήστης"
-
-#, fuzzy
+msgstr ""
+
 msgid "File patterns"
-msgstr "Τύπος Αρχείου"
+msgstr "Μοτίβα αρχείου"
 
 msgid "Mimetypes"
 msgstr "Mimetypes"
@@ -3366,10 +3429,10 @@
 
 #, python-format
 msgid "%(mins)dm ago"
-msgstr "%(mins)dm πρίν"
+msgstr "%(mins)dm πριν"
 
 msgid "(no bookmark set)"
-msgstr "(δεν ορίστικε κάποιος σελιδοδείκτης)"
+msgstr "(δεν ορίστηκε κάποιος σελιδοδείκτης)"
 
 #, python-format
 msgid "(currently set to %s)"
@@ -3404,18 +3467,18 @@
 msgstr "Αριθμός σελίδων συστήματος"
 
 msgid "Accumulated page sizes"
-msgstr ""
+msgstr "Συσσωρευμένα μεγέθη σελίδων"
 
 #, python-format
 msgid "Disk usage of %(data_dir)s/pages/"
-msgstr "Χρηση δίσκου του %(data_dir)s/pages/"
+msgstr "Χρήση δίσκου του %(data_dir)s/pages/"
 
 #, python-format
 msgid "Disk usage of %(data_dir)s/"
 msgstr "Χρήση δίσκου του %(data_dir)s/"
 
 msgid "Entries in edit log"
-msgstr "Αλλαγές στο πρακτικό με τις επεξεργασίες"
+msgstr "Καταχωρήσεις στο αρχείο καταγραφής με τις επεξεργασίες"
 
 msgid "NONE"
 msgstr "ΚΑΝΕΝΑ"
@@ -3430,16 +3493,16 @@
 msgstr ""
 
 msgid "Local extension actions"
-msgstr "Εντολές τοπικής επέκτασης"
+msgstr ""
 
 msgid "Global parsers"
-msgstr ""
+msgstr "Καθολικοί συντακτικοί αναλυτές"
 
 msgid "Local extension parsers"
 msgstr ""
 
 msgid "Xapian and/or Python Xapian bindings not installed"
-msgstr ""
+msgstr "Το Xapian και/η τα Python Xapian bindings δεν έχουν εγκατασταθεί"
 
 msgid "Disabled"
 msgstr "Απενεργοποιημένο"
@@ -3461,13 +3524,13 @@
 msgstr "Αναζήτηση Xapian"
 
 msgid "Stemming for Xapian"
-msgstr ""
+msgstr "Stemming για το Xapian"
 
 msgid "Active threads"
 msgstr "Ενεργά θέματα"
 
 msgid "No wanted pages in this wiki."
-msgstr "Δεν υπάρχουν ζητούμενες σελίδες σε αυτο το wiki."
+msgstr "Δεν υπάρχουν ζητούμενες σελίδες σε αυτό το wiki"
 
 msgid "Search for items"
 msgstr "Αναζήτηση στοιχείων"
@@ -3476,13 +3539,13 @@
 msgstr "περιέχει όλους τους παρακάτω όρους"
 
 msgid "containing one or more of the following terms"
-msgstr "περιέχει ένα ή περισσότερους από τους παρκάτω όρους "
+msgstr "περιέχει ένα ή περισσότερους από τους παρακάτω όρους "
 
 msgid "not containing the following terms"
 msgstr "δεν περιέχει τους παρακάτω όρους"
 
 msgid "last modified since (e.g. 2 weeks before)"
-msgstr "τελευταία τροποποίηση εδώ και (π.χ. last 2 weeks)"
+msgstr "τελευταία τροποποίηση εδώ και (π.χ. 2 weeks before)"
 
 msgid "any category"
 msgstr "οποιαδήποτε κατηγορία"
@@ -3506,7 +3569,7 @@
 msgstr "Αναζήτηση μικρών-ΚΕΦΑΛΑΙΩΝ"
 
 msgid "Exclude underlay"
-msgstr ""
+msgstr "Εξαίρεση υποστρώματος"
 
 msgid "No system items"
 msgstr "Δεν υπάρχουν αντικείμενα του συστήματος"
@@ -3525,6 +3588,8 @@
 "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 ""
+"Αποτελέσματα %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s από %(aboutHits)s %(bs)s"
+"%(hits)d%(be)s αποτελέσματα από περίπου %(pages)d σελίδες"
 
 msgid "seconds"
 msgstr "δευτερόλεπτα"
@@ -3543,71 +3608,3 @@
 
 msgid "about"
 msgstr "περί"
-
-#~ msgid "Page Name"
-#~ msgstr "Όνομα Σελίδας"
-
-#~ msgid "you may need to login to edit this page"
-#~ msgstr "πρέπει πρώτα να συνδεθείτε για να επεξεργαστείτε αυτή τη σελίδα."
-
-#~ msgid "BadContent"
-#~ msgstr "ΚακόΠεριεχόμενο"
-
-#, fuzzy
-#~ msgid "LocalBadContent"
-#~ msgstr "Περιεχόμενο"
-
-#, fuzzy
-#~ msgid "EditedSystemPages"
-#~ msgstr "Να αποκλεισθούν οι σελίδες συστήματος"
-
-#~ msgid "Created the package %s containing the pages %s."
-#~ msgstr "Δημιουργήθηκε το πακέτο %s,όπου περιέχονται οι σελίδες %s."
-
-#~ msgid "Restore"
-#~ msgstr "Επαναφορά"
-
-#~ msgid "Found no account matching the given email address '%(email)s'!"
-#~ msgstr ""
-#~ "Δε βρέθηκε λογαριασμός που να αντιστοιχεί στην email διεύθυνση '%(email)"
-#~ "s'!"
-
-#~ msgid "Use UserPreferences to change your settings or create an account."
-#~ msgstr ""
-#~ "Για να αλλάξετε τις προτιμήσεις σας ή να δημιουργήσετε καινούριο "
-#~ "λογιαριασμό χρησιμοποιείστε την επιλογή UserPreferences."
-
-#~ msgid "Use UserPreferences to change settings of the selected user account"
-#~ msgstr ""
-#~ "Χρησιμοποιήστε την επιλογή UserPreferences για να αλλάξετε τις ρυθμίσεις "
-#~ "για τον επιλεγμένο λογαριασμό χρήστη"
-
-#~ msgid ""
-#~ "This list does not work, unless you have entered a valid email address!"
-#~ msgstr ""
-#~ "Αυτή η λίστα δε μπορεί να χρησιμοποιηθεί αν δε βάλετε μια έγκυρη "
-#~ "διεύθυνση email!"
-
-#~ msgid ""
-#~ "To create an account, see the %(userprefslink)s page. To recover a lost "
-#~ "password, go to %(sendmypasswordlink)s."
-#~ msgstr ""
-#~ "Για να φτιάξετε έναν νέο λογαριασμό, δείτε τη σελίδα %(userprefslink)s. "
-#~ "Αν ξεχάσατε και θέλετε να ανακτήσετε τον κωδικικό σας, δείτε στο "
-#~ "%(sendmypasswordlink)s."
-
-#~ msgid "ERROR in regex '%s'"
-#~ msgstr "ΣΦΑΛΜΑ στην regex '%s'"
-
-#~ msgid "Bad timestamp '%s'"
-#~ msgstr "Λάθος timestamp '%s'"
-
-#~ msgid "Not supported mimetype of file: %s"
-#~ msgstr "Δεν υποστηρίζεται ο mimetype του αρχείου: %s"
-
-#~ msgid ""
-#~ "The remote version of MoinMoin is too old, version 1.6 is required at "
-#~ "least."
-#~ msgstr ""
-#~ "Η έκδοση του MoinMoin είναι πολύ παλιά, απαιτείται τουλάχιστον η έκδοση "
-#~ "1.6."
--- a/MoinMoin/i18n/en.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/en.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -11,7 +11,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.5\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\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"
@@ -431,12 +431,7 @@
 msgstr ""
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
 msgstr ""
 
 msgid ""
@@ -448,7 +443,12 @@
 msgstr ""
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
 msgstr ""
 
 #, python-format
--- a/MoinMoin/i18n/es.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/es.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2010-12-25 04:00+0300\n"
 "Last-Translator: Nicolae Antonio <anto.nicolae@gmail.com>\n"
 "Language-Team: Spanish\n"
@@ -487,19 +487,8 @@
 msgstr "<desconocido>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Login Nombre: %s\n"
-"\n"
-"Recuperación de Contraseña token: %s\n"
-"\n"
-"Reinicialización de Contraseña URL: %s?action=recoverpass&name=%s&token=%s\n"
-"\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "Tus datos de la cuenta del wiki en [%(sitename)s]"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -517,8 +506,19 @@
 "y la cadena de recuperación de contraseña.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "Tus datos de la cuenta del wiki en [%(sitename)s]"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Login Nombre: %s\n"
+"\n"
+"Recuperación de Contraseña token: %s\n"
+"\n"
+"Reinicialización de Contraseña URL: %s?action=recoverpass&name=%s&token=%s\n"
+"\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/fa.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/fa.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: 1.7.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2005-07-22 17:11+0330\n"
 "Last-Translator: Mehdi Hassanpour <h.mehdi@gmail.com>\n"
 "Language-Team: Persian <h.mehdi@gmail.com>\n"
@@ -473,18 +473,8 @@
 msgstr "<نامشخص>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"نام کاربری: %s\n"
-"\n"
-"رمز بازیافت کلمه عبور: %s\n"
-"\n"
-"آدرس بازنشانی کلمه عبور: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] اطلاعات مربوط به حساب کاربری شما"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -501,8 +491,18 @@
 "و رمز بازیافت کلمه عبور را وارد نمایید.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] اطلاعات مربوط به حساب کاربری شما"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"نام کاربری: %s\n"
+"\n"
+"رمز بازیافت کلمه عبور: %s\n"
+"\n"
+"آدرس بازنشانی کلمه عبور: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/fi.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/fi.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2010-02-13 23:15+0200\n"
 "Last-Translator: Ville-Pekka Vainio <vpivaini@cs.helsinki.fi>\n"
 "Language-Team: Finnish <laatu@lokalisointi.org>\n"
@@ -475,18 +475,8 @@
 msgstr "<tuntematon>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Kirjautumisnimi: %s\n"
-"\n"
-"Salasanan palautustunniste: %s\n"
-"\n"
-"Vaihda salasana osoitteessa %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Wiki-tunnuksesi tiedot"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -502,8 +492,18 @@
 "syötä käyttäjätunnuksesi sekä palautustunniste.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Wiki-tunnuksesi tiedot"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Kirjautumisnimi: %s\n"
+"\n"
+"Salasanan palautustunniste: %s\n"
+"\n"
+"Vaihda salasana osoitteessa %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
@@ -1191,7 +1191,8 @@
 
 msgid ""
 "Supplying a comment is mandatory.  Write a comment below and try again..."
-msgstr "Kommentin antaminen on pakollista. Kirjoita kommentti ja yritä uudelleen..."
+msgstr ""
+"Kommentin antaminen on pakollista. Kirjoita kommentti ja yritä uudelleen..."
 
 #, python-format
 msgid "[%d attachments]"
--- a/MoinMoin/i18n/fr.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/fr.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,8 +13,8 @@
 msgstr ""
 "Project-Id-Version: moin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
-"PO-Revision-Date: 2010-11-09 22:37+0100\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
+"PO-Revision-Date: 2013-02-15 00:40+0100\n"
 "Last-Translator: Jean-Philippe Guérard <fevrier@tigreraye.org>\n"
 "Language-Team: Français <>\n"
 "Language: \n"
@@ -300,10 +300,10 @@
 msgid ""
 "Text matching regular expression \"%(regex)s\" is highlighted. "
 "%(switch_link)s."
-msgstr ""
+msgstr "Le texte correspondant à \"%(regex)s\" est surligné. %(switch_link)s."
 
 msgid "Switch to non-highlighted view"
-msgstr ""
+msgstr "Passer à la vue non surlignée"
 
 msgid ""
 "The backed up content of this page is deprecated and will rank lower in "
@@ -496,19 +496,8 @@
 msgstr "<inconnu>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Nom d'utilisateur : %s\n"
-"\n"
-"Code de récupération du mot de passe : %s\n"
-"\n"
-"URL de remise à zéro du mot de passe : %s?action=recoverpass&name=%s&token="
-"%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Vos identifiants de connexion au wiki"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -526,8 +515,19 @@
 "code de récupération.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Vos identifiants de connexion au wiki"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Nom d'utilisateur : %s\n"
+"\n"
+"Code de récupération du mot de passe : %s\n"
+"\n"
+"URL de remise à zéro du mot de passe : %s?action=recoverpass&name=%s&token="
+"%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
@@ -1236,13 +1236,14 @@
 msgid "Install language packs for '%s'"
 msgstr "Installation des paquets pour la langue « %s »"
 
-#, fuzzy
 msgid "TextCha: Wrong answer! Try again below..."
-msgstr "TextCha : mauvaise réponse ! Revenez en arrière et réessayez..."
+msgstr "TextCha : mauvaise réponse ! Réessayez ci-dessous..."
 
 msgid ""
 "Supplying a comment is mandatory.  Write a comment below and try again..."
 msgstr ""
+"Un commentaire est obligatoire. Écrivez un commentaire ci-dessous et "
+"réessayez..."
 
 #, python-format
 msgid "[%d attachments]"
@@ -2837,9 +2838,8 @@
 msgid "HelpOnNotification"
 msgstr "AideDesNotifications"
 
-#, fuzzy
 msgid "HelpOnRobots"
-msgstr "AideDesFiletsHorizontaux"
+msgstr "AideDesRobots"
 
 msgid "HelpOnSessions"
 msgstr "AideDesSessions"
@@ -3525,15 +3525,17 @@
 msgid "Search Text"
 msgstr "Recherche de texte"
 
-#, fuzzy, python-format
+#, python-format
 msgid "Unknown macro parameter: %s."
-msgstr "Sous-action de sauvegarde inconnue : %s."
+msgstr "Paramètre de macro inconnu : %s."
 
 #, python-format
 msgid ""
 "More than one needle with search_macro_parse_args config option enabled "
 "('%(needle)s' found already, '%(arg)s' occurred)"
 msgstr ""
+"L'option search_macro_parse_args est activée et il y a plus d'une aiguille "
+"('%(needle)s' déjà trouvé, '%(arg)s' est survenu)"
 
 msgid "Lexer description"
 msgstr "Description de l'analyseur lexical"
--- a/MoinMoin/i18n/gl.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/gl.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2007-02-22 17:39-0300\n"
 "Last-Translator: Iván Méndez <imendez@udc.es>\n"
 "Language-Team: Galician <moin-devel@lists.sourceforge.net>\n"
@@ -478,6 +478,18 @@
 msgid "<unknown>"
 msgstr "<descoñecido>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "Os seus datos da conta do wiki en [%(sitename)s]"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -492,18 +504,6 @@
 "\n"
 "URL de ingreso: %s/%s?action=login\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "Os seus datos da conta do wiki en [%(sitename)s]"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr ""
--- a/MoinMoin/i18n/he.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/he.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.7\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-08-22 02:05+0300\n"
 "Last-Translator: Dror Livne <livne.dror@gmail.com>\n"
 "Language-Team: Hebrew <moin-user@lists.sourceforge.net>\n"
@@ -457,18 +457,8 @@
 msgstr "<לא ידוע>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"שם משתמש: %s\n"
-"\n"
-"מפתח לאיפוס ססמה: %s\n"
-"\n"
-"כתובת לאיפוס ססמה: %s/action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] פרטי המשתמש שלך"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -483,8 +473,18 @@
 "והכנס את שם המשתמש שלך ואת מפתח האיפוס.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] פרטי המשתמש שלך"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"שם משתמש: %s\n"
+"\n"
+"מפתח לאיפוס ססמה: %s\n"
+"\n"
+"כתובת לאיפוס ססמה: %s/action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/hi.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/hi.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: \n"
 "Last-Translator: hppy <nawanihappy@gmail.com>\n"
 "Language-Team: \n"
@@ -466,18 +466,8 @@
 msgstr "<अज्ञात>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"लॉगिन नाम: %s\n"
-"\n"
-"password पुनर्प्राप्ति टोकन: %s\n"
-"\n"
-"password  रीसेट URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] आपके विकी खाते का डेटा "
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -494,8 +484,18 @@
 "पुनर्प्राप्त टोकन दर्ज करें| \n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] आपके विकी खाते का डेटा "
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"लॉगिन नाम: %s\n"
+"\n"
+"password पुनर्प्राप्ति टोकन: %s\n"
+"\n"
+"password  रीसेट URL: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/hr.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/hr.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.7\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2006-08-17 20:50+0100\n"
 "Last-Translator: Translation sponsored by Tenalt Ltd. (info@tenalt.com)\n"
 "Language-Team: \n"
@@ -469,6 +469,23 @@
 msgid "<unknown>"
 msgstr "<nepoznato>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Podaci o vašem korisničkom računu"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+"Netko je na ovaj email zatražio šifru za promjenu lozinke.\n"
+"\n"
+"Ako ste izgubili svoju lozinku, posjetite URL za reset logine ili\n"
+"ili na stranici za zahtijevanje lozinke upišite svoje korisničko\n"
+"ime i šifru koju ste dobili u ovoj poruci.\n"
+
 # python-format
 #, fuzzy, python-format
 msgid ""
@@ -484,23 +501,6 @@
 "\n"
 "URL za prijavu: %s/?action=recoverpass&name=%s&token=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-"Netko je na ovaj email zatražio šifru za promjenu lozinke.\n"
-"\n"
-"Ako ste izgubili svoju lozinku, posjetite URL za reset logine ili\n"
-"ili na stranici za zahtijevanje lozinke upišite svoje korisničko\n"
-"ime i šifru koju ste dobili u ovoj poruci.\n"
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Podaci o vašem korisničkom računu"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Morate se prijaviti za korištenje pretplate na stranicu."
--- a/MoinMoin/i18n/hu.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/hu.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2006-06-15 15:55+0200\n"
 "Last-Translator: MARTON Jozsef <jmarton@omikk.bme.hu>\n"
 "Language-Team: Hungarian <HU@li.org>\n"
@@ -462,17 +462,9 @@
 msgid "<unknown>"
 msgstr "<ismeretlen>"
 
-#, fuzzy, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Belépési név: %s \n"
-"Jelszóváltoztatási zseton (token): %s\n"
-"Jelszóváltoztatási URL: %s/?action=recoverpass&name=%s&token=%s\n"
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] a wiki azonosítójának adata"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -487,9 +479,17 @@
 "vagy látogasson el újra a jelszóváltoztatási lapra, és írja be\n"
 "a felhazsnálói nevét és a zseton kódját.\n"
 
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] a wiki azonosítójának adata"
+#, fuzzy, python-format
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Belépési név: %s \n"
+"Jelszóváltoztatási zseton (token): %s\n"
+"Jelszóváltoztatási URL: %s/?action=recoverpass&name=%s&token=%s\n"
 
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/id.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/id.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2006-10-07 23:19+0700\n"
 "Last-Translator: Fajrin Azis <fajrin.azis@gmail.com>\n"
 "Language-Team: Indonesian <id@ll.com>\n"
@@ -484,6 +484,18 @@
 msgid "<unknown>"
 msgstr "<tak diketahui>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Data akun wiki Anda"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -498,18 +510,6 @@
 "\n"
 "URL untuk Login: %s/%s?action=login\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Data akun wiki Anda"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Anda harus log in untuk dapat berlangganan."
--- a/MoinMoin/i18n/it.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/it.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,8 +13,8 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
-"PO-Revision-Date: 2012-09-18 22:48+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
+"PO-Revision-Date: 2012-09-24 22:48+0200\n"
 "Last-Translator: Nico Zanferrari <nicozanf@gmail.com>\n"
 "Language-Team: Italian <tp@lists.linux.it>\n"
 "Language: it\n"
@@ -482,18 +482,8 @@
 msgstr "<informazione non disponibile>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Nome di accesso: %s\n"
-"\n"
-"Token recupero password: %s\n"
-"\n"
-"URL di ripristino password: %s/?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Dati del tuo account wiki"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -513,8 +503,18 @@
 "di recupero.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Dati del tuo account wiki"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Nome di accesso: %s\n"
+"\n"
+"Token recupero password: %s\n"
+"\n"
+"URL di ripristino password: %s/?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
@@ -1201,13 +1201,14 @@
 msgid "Install language packs for '%s'"
 msgstr "Installazione pacchetto lingua per «%s»"
 
-#, fuzzy
 msgid "TextCha: Wrong answer! Try again below..."
-msgstr "TextCha: risposta sbagliata. Torna indietro e riprova..."
+msgstr "TextCha: risposta sbagliata. Riprovare nuovamente qui sotto..."
 
 msgid ""
 "Supplying a comment is mandatory.  Write a comment below and try again..."
 msgstr ""
+"É obbligatorio inserire un commento. Scrivere un commento qui sotto e "
+"riprovare..."
 
 #, python-format
 msgid "[%d attachments]"
--- a/MoinMoin/i18n/ja.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/ja.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2011-02-02 05:05+0900\n"
 "Last-Translator: HiroshiIwasaki <iwskhrs@gmail.com>\n"
 "Language-Team: Japanese <moin-devel@lists.sourceforge.net>\n"
@@ -471,18 +471,8 @@
 msgstr "<不明>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"ログイン名: %s\n"
-"\n"
-"パスワード回復トークン: %s\n"
-"\n"
-"パスワード初期化URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] アカウント情報"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -498,8 +488,18 @@
 "い。\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] アカウント情報"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"ログイン名: %s\n"
+"\n"
+"パスワード回復トークン: %s\n"
+"\n"
+"パスワード初期化URL: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/ko.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/ko.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2010-03-13 11:16+0900\n"
 "Last-Translator: YongjinCho <yongjin.cho@gmail.com>\n"
 "Language-Team: Korean <moin-devel@lists.sourceforge.net>\n"
@@ -457,6 +457,18 @@
 msgid "<unknown>"
 msgstr "<알 수 없음>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] 위키 계정 정보"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -471,18 +483,6 @@
 "\n"
 "로그인 URL: %s/%s?action=login\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] 위키 계정 정보"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "문서의 변경 사항을 구독하기 위해서는 로그인을 하셔야 합니다."
--- a/MoinMoin/i18n/ku.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/ku.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2006-12-08 18:50+0000\n"
 "Last-Translator: Erdal Ronahi <erdal.ronahi@gmail.com>\n"
 "Language-Team: Kurdish <ku@li.org>\n"
@@ -457,6 +457,18 @@
 msgid "<unknown>"
 msgstr "<nenas>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Agahiyên hesabê te yê wikiyê"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -471,18 +483,6 @@
 "\n"
 "Ji Bo Forma Têketinê URL %s/?action=userform&uid=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Agahiyên hesabê te yê wikiyê"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Ji bo nîşandana vî rûpelî têra xwe destûra te tuneye."
--- a/MoinMoin/i18n/lt.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/lt.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2009-06-06 01:00+0200\n"
 "Last-Translator: Ernestas Liubarskij <e.liubarskij@gmail.com>\n"
 "Language-Team: Lithuanian <e.liubarskij@gmail.com>\n"
@@ -474,6 +474,23 @@
 msgid "<unknown>"
 msgstr "<nežinomas>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Jūsų wiki paskyros duomenys"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+"Kažkas pareikalavo el. paštu išsiųsti jums slaptažodžio atkūrimo raktą.\n"
+"\n"
+"Jeigu pamiršote savo slaptažodį, apsilankykite žemiau nurodytu slaptažodžio\n"
+"atkūrimo adresu arba grįžkite į slaptažodžio atkūrimo puslapį ir ten\n"
+"įveskite savo naudotojo vardą bei atkūrimo raktą.\n"
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -488,23 +505,6 @@
 "\n"
 "Slaptažodžio atkūrimo adresas: %s/?action=recoverpass&name=%s&token=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-"Kažkas pareikalavo el. paštu išsiųsti jums slaptažodžio atkūrimo raktą.\n"
-"\n"
-"Jeigu pamiršote savo slaptažodį, apsilankykite žemiau nurodytu slaptažodžio\n"
-"atkūrimo adresu arba grįžkite į slaptažodžio atkūrimo puslapį ir ten\n"
-"įveskite savo naudotojo vardą bei atkūrimo raktą.\n"
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Jūsų wiki paskyros duomenys"
-
 #, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Jūs privalote prisijungti, norėdami atlikti šį veiksmą:  %(action)s."
--- a/MoinMoin/i18n/lv.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/lv.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.7\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-05-10 22:31+0200\n"
 "Last-Translator: Radomirs Cirskis <nad2000 AT AT AT AT gmail SPAM NO NO DOT "
 "NO SPAM com>\n"
@@ -474,19 +474,9 @@
 msgid "<unknown>"
 msgstr "<nezināms>"
 
-#, fuzzy, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Lietotāja vārds: %s\n"
-"\n"
-"Parole atjaunošanas marķieris: %s\n"
-"\n"
-"Paroles atstatīšanas URL: %s/?action=recoverpass&name=%s&token=%s\n"
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Jūsu viki konta dati"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -503,9 +493,19 @@
 "vārdu un\n"
 "atjaunošanas \"marķieri\".\n"
 
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Jūsu viki konta dati"
+#, fuzzy, python-format
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Lietotāja vārds: %s\n"
+"\n"
+"Parole atjaunošanas marķieris: %s\n"
+"\n"
+"Paroles atstatīšanas URL: %s/?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/mk.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/mk.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: moin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-06-27 22:39+0200\n"
 "Last-Translator: Novica Nakov\n"
 "Language-Team: Macedonian <ossm-members@hedona.on.net.mk>\n"
@@ -474,6 +474,24 @@
 msgid "<unknown>"
 msgstr "<непознато>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Потадоци за Вашата корисничка сметка"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+"Некој побарал да добиете белег за пронаоѓање на лозинката.\n"
+"\n"
+"Ако сте ја загубиле лозинката, одете до URL-то за ресетирање на лозинката\n"
+"или повторно одете до страницата за пронаоѓање на лозинки и внесете го "
+"Вашето\n"
+"корисничко име и белегот за пронаоѓање.\n"
+
 # python-format
 #, fuzzy, python-format
 msgid ""
@@ -489,24 +507,6 @@
 "\n"
 "URL за ресетирање на лозинка: %s/?action=recoverpass&name=%s&token=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-"Некој побарал да добиете белег за пронаоѓање на лозинката.\n"
-"\n"
-"Ако сте ја загубиле лозинката, одете до URL-то за ресетирање на лозинката\n"
-"или повторно одете до страницата за пронаоѓање на лозинки и внесете го "
-"Вашето\n"
-"корисничко име и белегот за пронаоѓање.\n"
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Потадоци за Вашата корисничка сметка"
-
 #, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Морате да се пријавите за можете да го направите ова: %(action)s."
--- a/MoinMoin/i18n/mn.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/mn.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2006-12-05 12:04+0800\n"
 "Last-Translator: Amgalanbaatar Delegdorj <d.amgalanbaatar@gmail.com>\n"
 "Language-Team: Mongolia <d.amgalanbaatar@gmail.com>\n"
@@ -474,6 +474,18 @@
 msgid "<unknown>"
 msgstr "<тодорхойгүй>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Таны Вики дансны мэдээлэл"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -488,18 +500,6 @@
 "\n"
 "Логин-URL: %s/%s?action=login\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Таны Вики дансны мэдээлэл"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Та Abonnements ашиглахын тулд бүртгүүлэх шаардлагатай."
--- a/MoinMoin/i18n/nb.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/nb.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-12-03 14:24+0100\n"
 "Last-Translator: Jörg Cassens <moinmoin-nb@cassens.org>\n"
 "Language-Team: Norwegian <moinmoin-nb@cassens.org>\n"
@@ -291,9 +291,11 @@
 "Text matching regular expression \"%(regex)s\" is highlighted. "
 "%(switch_link)s."
 msgstr ""
+"Tekst som tilfredsstiller det regulære uttrykket \"%(regex)s\" er fremhevet. "
+"%(switch_link)s."
 
 msgid "Switch to non-highlighted view"
-msgstr ""
+msgstr "Bytt til en ikke-fremhevet fremvisning"
 
 msgid ""
 "The backed up content of this page is deprecated and will rank lower in "
@@ -472,18 +474,8 @@
 msgstr "<ukjent>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Brukernavn: %s\n"
-"\n"
-"Passord-gjenopprettingsord: %s\n"
-"\n"
-"Passord-tilbakestillings-URL: %s/?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Din wiki brukerdata"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -499,8 +491,18 @@
 "brukernavnet ditt og passord-gjenopprettingsordet.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Din wiki brukerdata"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Brukernavn: %s\n"
+"\n"
+"Passord-gjenopprettingsord: %s\n"
+"\n"
+"Passord-tilbakestillings-URL: %s/?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
@@ -1186,6 +1188,8 @@
 msgid ""
 "Supplying a comment is mandatory.  Write a comment below and try again..."
 msgstr ""
+"En kommentar må oppgis. Vennligst skriv en kommentar nedenfor og prøv "
+"igjen..."
 
 #, python-format
 msgid "[%d attachments]"
--- a/MoinMoin/i18n/nl.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/nl.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2012-06-12 10:41+0200\n"
 "Last-Translator: Rachid BM <rachidbm@ubuntu.com>\n"
 "Language-Team: Dutch <vertaling@vrijschrift.org>\n"
@@ -480,18 +480,8 @@
 msgstr "<onbekend>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Inlognaam: %s\n"
-"\n"
-"Herstelcode voor wachtwoord: %s\n"
-"\n"
-"URL voor wachtwoordherstel: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Uw wiki account informatie"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -510,8 +500,18 @@
 "gebruikersnaam en herstelcode in.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Uw wiki account informatie"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Inlognaam: %s\n"
+"\n"
+"Herstelcode voor wachtwoord: %s\n"
+"\n"
+"URL voor wachtwoordherstel: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/pl.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/pl.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: 1.8\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2006-02-09 11:25+0100\n"
 "Last-Translator: Sergiusz Pawłowicz <moin@pawlowicz.name>\n"
 "Language-Team: Polish <moin@pawlowicz.name>\n"
@@ -473,18 +473,8 @@
 msgstr "<nieznany>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Nazwa czytelnika: %s\n"
-"\n"
-"Kod do odzyskiwania hasła: %s\n"
-"\n"
-"Adres do resetowania hasła: %s/?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Twoje dane czytelnika"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -500,8 +490,18 @@
 "i kod odzyskiwania hasła.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Twoje dane czytelnika"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Nazwa czytelnika: %s\n"
+"\n"
+"Kod do odzyskiwania hasła: %s\n"
+"\n"
+"Adres do resetowania hasła: %s/?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/pt-br.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/pt-br.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2009-09-28\n"
 "Last-Translator: Renato Silva\n"
 "Language-Team: Português Brasileiro <moin-devel@lists.sourceforge.net\n"
@@ -477,18 +477,8 @@
 msgstr "<desconhecido>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Login: %s\n"
-"\n"
-"Token para recuperação da senha: %s\n"
-"\n"
-"URL para reiniciar a senha: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Seus dados da conta do wiki"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -504,8 +494,18 @@
 "login e a chave de recuperação de senha.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Seus dados da conta do wiki"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Login: %s\n"
+"\n"
+"Token para recuperação da senha: %s\n"
+"\n"
+"URL para reiniciar a senha: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/pt.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/pt.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2007-01-05 22:27+0000\n"
 "Last-Translator: Rita Farinha <rita.farinha@intraneia.com>\n"
 "Language-Team: Portuguese <moin-devel@lists.sourceforge.net>\n"
@@ -479,6 +479,18 @@
 msgid "<unknown>"
 msgstr "<desconhecido>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Os dados da sua conta no wiki"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -493,18 +505,6 @@
 "\n"
 "URL de acesso: %s/%s?action=login\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Os dados da sua conta no wiki"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Tem de autenticar-se para subscrever páginas."
--- a/MoinMoin/i18n/ro.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/ro.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2010-12-24 23:47+0300\n"
 "Last-Translator: Nicolae Antonio <anto.nicolae@gmail.com>\n"
 "Language-Team: Romanian\n"
@@ -485,18 +485,8 @@
 msgstr "<necunoscut>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Nume utilizator: %s\n"
-"\n"
-"Codul de recuperare al parolei: %s\n"
-"\n"
-"URL de reconfigurare a parolei: %s/?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Datele contului wiki al dumneavoastră"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -514,8 +504,18 @@
 "codul de recuperare.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Datele contului wiki al dumneavoastră"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Nume utilizator: %s\n"
+"\n"
+"Codul de recuperare al parolei: %s\n"
+"\n"
+"URL de reconfigurare a parolei: %s/?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/ru.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/ru.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2011-01-27 16:25+0300\n"
 "Last-Translator: Eugene Syromyatnikov <evgsyr@gmail.com>\n"
 "Language-Team: Russian <moin-devel@lists.sourceforge.net>\n"
@@ -485,18 +485,8 @@
 msgstr "<???>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Имя: %s\n"
-"\n"
-"Строка сброса пароля: %s\n"
-"\n"
-"URL для сброса пароля: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Данные Вашей учетной записи"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -513,8 +503,18 @@
 "строку сброса пароля.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Данные Вашей учетной записи"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Имя: %s\n"
+"\n"
+"Строка сброса пароля: %s\n"
+"\n"
+"URL для сброса пароля: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
@@ -1197,7 +1197,9 @@
 
 msgid ""
 "Supplying a comment is mandatory.  Write a comment below and try again..."
-msgstr "Указание комментария к изменениям обязательно. Пожалуйста, укажите комментарий в соотвествующем поле и попробуйте ещё раз."
+msgstr ""
+"Указание комментария к изменениям обязательно. Пожалуйста, укажите "
+"комментарий в соотвествующем поле и попробуйте ещё раз."
 
 #, python-format
 msgid "[%d attachments]"
--- a/MoinMoin/i18n/sk.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/sk.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: moin 1.8\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-11-01 11:35+0100\n"
 "Last-Translator: Radovan Garabík <rg-m18@kassiopeia.juls.savba.sk>\n"
 "Language-Team: Slovak\n"
@@ -470,6 +470,23 @@
 msgid "<unknown>"
 msgstr "<neznámy>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Údaje vášho wiki účtu"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+"Niekto zadal požiadavku na odoslanie tokenu pre obnovu hesla.\n"
+"\n"
+"Ak ste zabudli heslo, navštívne URL pre resetovanie hesla alebo\n"
+"stránku pre obnovu hesla a zadajte svoje užívateľské meno\n"
+"a token pre obnovu.\n"
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -484,23 +501,6 @@
 "\n"
 "URL pre resetovanie hesla: %s/?action=recoverpass&name=%s&token=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-"Niekto zadal požiadavku na odoslanie tokenu pre obnovu hesla.\n"
-"\n"
-"Ak ste zabudli heslo, navštívne URL pre resetovanie hesla alebo\n"
-"stránku pre obnovu hesla a zadajte svoje užívateľské meno\n"
-"a token pre obnovu.\n"
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Údaje vášho wiki účtu"
-
 #, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Použiť akciu %(action)s možno až po prihlásení."
--- a/MoinMoin/i18n/sl.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/sl.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-05-23 03:00+0200\n"
 "Last-Translator: Mark Martinec <Mark.Martinec@ijs.si>\n"
 "Language-Team: \n"
@@ -470,18 +470,8 @@
 msgstr "<neznano>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Uporabniško ime: %s\n"
-"\n"
-"Žeton za obnovo gesla: %s\n"
-"\n"
-"URL za obnovo gesla: %s/?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Podatki o vašem viki uporabniškem imenu (računu)"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -497,8 +487,18 @@
 "in žeton za obnovo gesla.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Podatki o vašem viki uporabniškem imenu (računu)"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Uporabniško ime: %s\n"
+"\n"
+"Žeton za obnovo gesla: %s\n"
+"\n"
+"URL za obnovo gesla: %s/?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/sr.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/sr.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: sr\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-05-12 15:58-0400\n"
 "Last-Translator: Igor Miletic <grejigl-gnomeprevod@yahoo.ca>\n"
 "Language-Team: Serbian <fedora-trans-sr@redhat.com>\n"
@@ -475,6 +475,23 @@
 msgid "<unknown>"
 msgstr "<непознато>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Подаци вашег вики налога"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+"Неко је поднео захтев за слање израза за опоравак лозинке преко ел. поште.\n"
+"\n"
+"Ако сте заборавили лозинку, идите на доњи УРЛ за постављање лозинке или\n"
+"поново отиђите на страницу за опоравак лозинке и унесите корисничко име и\n"
+"израз за опоравак.\n"
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -489,23 +506,6 @@
 "\n"
 "УРЛ за постављање лозинке: %s/?action=recoverpass&name=%s&token=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-"Неко је поднео захтев за слање израза за опоравак лозинке преко ел. поште.\n"
-"\n"
-"Ако сте заборавили лозинку, идите на доњи УРЛ за постављање лозинке или\n"
-"поново отиђите на страницу за опоравак лозинке и унесите корисничко име и\n"
-"израз за опоравак.\n"
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Подаци вашег вики налога"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Морате се пријавити да бисте користили претплате."
--- a/MoinMoin/i18n/sv.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/sv.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2010-10-13 22:01+0200\n"
 "Last-Translator: Thomas Marquart <thomas@marquart.se>\n"
 "Language-Team: Swedish <moin-user@lists.sourceforge.net>\n"
@@ -470,18 +470,8 @@
 msgstr "<okänd>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"Användarnamn: %s\n"
-"\n"
-"Token för att återställa lösenord: %s\n"
-"\n"
-"URL för att återställa lösenord: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Din wiki-kontoinformation"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -499,8 +489,18 @@
 "token för återställning.\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Din wiki-kontoinformation"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"Användarnamn: %s\n"
+"\n"
+"Token för att återställa lösenord: %s\n"
+"\n"
+"URL för att återställa lösenord: %s?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/i18n/tr.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/tr.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin-1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2007-04-25 16:26+0300\n"
 "Last-Translator: Ekrem SEREN <ekrem.seren@parduslinux.org>\n"
 "Language-Team: Türkçe\n"
@@ -486,6 +486,18 @@
 msgid "<unknown>"
 msgstr "<bilinmiyor>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Wiki hesap bilgileriniz"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -500,18 +512,6 @@
 "\n"
 "Giriş için bağlantı: %s/%s?action=login\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Wiki hesap bilgileriniz"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Sayfa üyeliği özelliğinden yararlanabilmek için giriş yapmalısınız."
--- a/MoinMoin/i18n/uk.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/uk.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2007-11-10 16:50+0300\n"
 "Last-Translator: Maxim V. Dziumanenko <dziumanenko@softprom.kiev.ua>\n"
 "Language-Team: Ukrainian <uk@li.org>\n"
@@ -476,6 +476,18 @@
 msgid "<unknown>"
 msgstr "<невідомо>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] Дані вашого облікового рахунку wiki"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -490,18 +502,6 @@
 "\n"
 "URL відновлення паролю: %s/?action=recoverpass&name=%s&token=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] Дані вашого облікового рахунку wiki"
-
 #, fuzzy, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "Зареєструйтесь, щоб мати доступ до: %(action)s."
--- a/MoinMoin/i18n/vi.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/vi.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2004-11-19 21:03+0800\n"
 "Last-Translator: Nam T. Nguyen <nnt@nntsoft.com>\n"
 "Language-Team: Vietnamese <moin-devel@lists.sourceforge.net>\n"
@@ -472,12 +472,7 @@
 msgstr "<không rõ>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
 msgstr ""
 
 msgid ""
@@ -489,7 +484,12 @@
 msgstr ""
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
 msgstr ""
 
 #, fuzzy, python-format
--- a/MoinMoin/i18n/zh-tw.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/zh-tw.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.7\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-06-14 22:44+0800\n"
 "Last-Translator: Rux Li <rux.li3@gmail.com>\n"
 "Language-Team: Chinese/Taiwan <moin-devel@lists.sourceforge.net>\n"
@@ -457,6 +457,18 @@
 msgid "<unknown>"
 msgstr "<不明>"
 
+#, python-format
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] 你的 wiki 帳號資料"
+
+msgid ""
+"Somebody has requested to email you a password recovery token.\n"
+"\n"
+"If you lost your password, please go to the password reset URL below or\n"
+"go to the password recovery page again and enter your username and the\n"
+"recovery token.\n"
+msgstr ""
+
 #, fuzzy, python-format
 msgid ""
 "Login Name: %s\n"
@@ -471,18 +483,6 @@
 "\n"
 "登入網址:%s/?action=recoverpass&name=%s&token=%s\n"
 
-msgid ""
-"Somebody has requested to email you a password recovery token.\n"
-"\n"
-"If you lost your password, please go to the password reset URL below or\n"
-"go to the password recovery page again and enter your username and the\n"
-"recovery token.\n"
-msgstr ""
-
-#, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] 你的 wiki 帳號資料"
-
 #, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr "登入之後才能使用此功能:%(action)s。"
--- a/MoinMoin/i18n/zh.MoinMoin.po	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/i18n/zh.MoinMoin.po	Sun Mar 24 14:58:56 2013 +0100
@@ -13,7 +13,7 @@
 msgstr ""
 "Project-Id-Version: MoinMoin 1.9\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-09-20 23:12+0200\n"
+"POT-Creation-Date: 2013-03-10 16:38+0100\n"
 "PO-Revision-Date: 2008-10-06 11:09+0800\n"
 "Last-Translator: Dormouse.Young <dormouseyoung@gmail.com>\n"
 "Language-Team: Chinese <moin-devel@lists.sourceforge.net>\n"
@@ -454,18 +454,8 @@
 msgstr "<未知>"
 
 #, python-format
-msgid ""
-"Login Name: %s\n"
-"\n"
-"Password recovery token: %s\n"
-"\n"
-"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
-msgstr ""
-"登录帐号:%s\n"
-"\n"
-"口令恢复令牌:%s\n"
-"\n"
-"口令重置 URL:%s/?action=recoverpass&name=%s&token=%s\n"
+msgid "[%(sitename)s] Your wiki account data"
+msgstr "[%(sitename)s] 您的维基帐号资料"
 
 msgid ""
 "Somebody has requested to email you a password recovery token.\n"
@@ -480,8 +470,18 @@
 "或者再次访问口令恢复页面输入用户名和恢复令牌。\n"
 
 #, python-format
-msgid "[%(sitename)s] Your wiki account data"
-msgstr "[%(sitename)s] 您的维基帐号资料"
+msgid ""
+"Login Name: %s\n"
+"\n"
+"Password recovery token: %s\n"
+"\n"
+"Password reset URL: %s?action=recoverpass&name=%s&token=%s\n"
+msgstr ""
+"登录帐号:%s\n"
+"\n"
+"口令恢复令牌:%s\n"
+"\n"
+"口令重置 URL:%s/?action=recoverpass&name=%s&token=%s\n"
 
 #, python-format
 msgid "You must login to use this action: %(action)s."
--- a/MoinMoin/log.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/log.py	Sun Mar 24 14:58:56 2013 +0100
@@ -58,10 +58,11 @@
 # See http://www.python.org/doc/lib/logging-config-fileformat.html
 # We just use stderr output by default, if you want anything else,
 # you will have to configure logging.
-logging_defaults = {
-    'loglevel': 'INFO',
-}
 logging_config = """\
+[DEFAULT]
+# Default loglevel, to adjust verbosity: DEBUG, INFO, WARNING, ERROR, CRITICAL
+loglevel=INFO
+
 [loggers]
 keys=root
 
@@ -120,7 +121,15 @@
     if conf_fname:
         try:
             conf_fname = os.path.abspath(conf_fname)
-            logging.config.fileConfig(conf_fname)
+            # we open the conf file here to be able to give a reasonable
+            # error message in case of failure (if we give the filename to
+            # fileConfig(), it silently ignores unreadable files and gives
+            # unhelpful error msgs like "No section: 'formatters'"):
+            f = open(conf_fname)
+            try:
+                logging.config.fileConfig(f)
+            finally:
+                f.close()
             configured = True
             l = getLogger(__name__)
             l.info('using logging configuration read from "%s"' % conf_fname)
@@ -130,15 +139,23 @@
     if not configured:
         # load builtin fallback logging config
         from StringIO import StringIO
-        config_file = StringIO(logging_config)
-        logging.config.fileConfig(config_file, logging_defaults)
+        f = StringIO(logging_config)
+        try:
+            logging.config.fileConfig(f)
+        finally:
+            f.close()
         configured = True
         l = getLogger(__name__)
         if err_msg:
             l.warning('load_config for "%s" failed with "%s".' % (conf_fname, err_msg))
-        l.warning('using logging configuration read from built-in fallback in MoinMoin.log module!')
+        l.info('using logging configuration read from built-in fallback in MoinMoin.log module')
         warnings.showwarning = _log_warning
 
+    import MoinMoin
+    code_path = os.path.dirname(MoinMoin.__file__)
+    from MoinMoin.version import project, release, revision
+    l.info('Running %s %s %s code from %s' % (project, release, revision, code_path))
+
 
 def getLogger(name):
     """ wrapper around logging.getLogger, so we can do some more stuff:
--- a/MoinMoin/logfile/eventlog.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/logfile/eventlog.py	Sun Mar 24 14:58:56 2013 +0100
@@ -29,7 +29,9 @@
         """ Write an event of type `eventtype, with optional key/value
             pairs appended (i.e. you have to pass a dict).
         """
-        if request.isSpiderAgent:
+        cfg = request.cfg
+        if cfg.log_events_format == 0 or request.isSpiderAgent:
+            # no event logging enabled or user agent is a bot / spider
             return
 
         if mtime_usecs is None:
@@ -37,7 +39,7 @@
 
         if values is None:
             values = {}
-        if request.cfg.log_remote_addr and add_http_info:
+        if cfg.log_remote_addr and add_http_info:
             # if cfg.log_remote_addr is False (usually for privacy reasons),
             # we likely do not want to log user agent and http referer either.
             for key in ['remote_addr', 'http_user_agent', 'http_referer']:
@@ -45,6 +47,12 @@
                 if value:
                     # Save those http headers in UPPERcase
                     values[key.upper()] = value
+
+        if cfg.log_events_format == 2:
+            values['username'] = request.user.name
+            values['wikiname'] = cfg.interwikiname
+            values['url'] = request.url
+
         # Encode values in a query string TODO: use more readable format
         values = wikiutil.makeQueryString(values)
         self._add(u"%d\t%s\t%s\n" % (mtime_usecs, eventtype, values))
--- a/MoinMoin/macro/AttachList.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/macro/AttachList.py	Sun Mar 24 14:58:56 2013 +0100
@@ -9,16 +9,19 @@
     If mime_type isn't given, all files are listed.
 
     @copyright: 2004 Jacob Cohen, Nigel Metheringham,
-                2006 MoinMoin:ReimarBauer
+                2006-2013 MoinMoin:ReimarBauer
     @license: GNU GPL, see COPYING for details.
 """
 
+import re
+
 from MoinMoin.action.AttachFile import _build_filelist
 
-def macro_AttachList(macro, pagename=None, mime_type=u'*'):
+def macro_AttachList(macro, pagename=None, mime_type=u'*', search_term=u'.+'):
     # defaults if we don't get anything better
     if not pagename:
         pagename = macro.formatter.page.page_name
+    filterfn = re.compile(search_term, re.U).search
+    return _build_filelist(macro.request, pagename, 0, 1, mime_type=mime_type, filterfn=filterfn)
 
-    return _build_filelist(macro.request, pagename, 0, 1, mime_type=mime_type)
 
--- a/MoinMoin/mail/sendmail.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/mail/sendmail.py	Sun Mar 24 14:58:56 2013 +0100
@@ -149,6 +149,9 @@
                 except AttributeError:
                     # in case the connection failed, SMTP has no "sock" attribute
                     pass
+        except UnicodeError, e:
+            logging.exception("unicode error [%r -> %r]" % (mail_from, to, ))
+            return (0, str(e))
         except smtplib.SMTPException, e:
             logging.exception("smtp mail failed with an exception.")
             return (0, str(e))
--- a/MoinMoin/parser/text_rst.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/parser/text_rst.py	Sun Mar 24 14:58:56 2013 +0100
@@ -75,8 +75,8 @@
     docutils = html4css1 = dummyUrllib2()
     html4css1.HTMLTranslator = html4css1.Writer = object
 
-def safe_import(name, globals = None, locals = None, fromlist = None):
-    mod = __builtin__.__import__(name, globals, locals, fromlist)
+def safe_import(name, globals = None, locals = None, fromlist = None, level = -1):
+    mod = __builtin__.__import__(name, globals, locals, fromlist, level)
     if mod:
         mod.open = dummyOpen
         mod.urllib2 = dummyUrllib2
--- a/MoinMoin/script/__init__.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/script/__init__.py	Sun Mar 24 14:58:56 2013 +0100
@@ -243,6 +243,11 @@
 """)
 
         cmd_module, cmd_name = args[:2]
+        if cmd_module == '...':
+            # our docs usually tell to use moin ... cmd_module cmd_name
+            # if somebody enters the ... verbatim, tell him how to do it right:
+            fatal("Wrong invokation. Please do not enter ... verbatim, but give --config-dir and --wiki-url options (see help for more details).")
+
         from MoinMoin import wikiutil
         try:
             plugin_class = wikiutil.importBuiltinPlugin('script.%s' % cmd_module, cmd_name, 'PluginScript')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/account/inactive.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,132 @@
+# -*- coding: iso-8859-1 -*-
+"""
+MoinMoin - find inactive users (and disable / remove them)
+
+@copyright: 2013 MoinMoin:ThomasWaldmann,
+@license: GNU GPL, see COPYING for details.
+"""
+
+import sys, os
+
+from MoinMoin.script import MoinScript, log
+from MoinMoin.user import getUserList, User
+from MoinMoin.logfile import editlog
+
+class PluginScript(MoinScript):
+    """\
+Purpose:
+========
+This tool allows you to find inactive users on your wiki via a command line interface.
+
+Inactive user means: a user profile with a specific userid exists, but there is not
+any edit logged for that userid.
+
+But please review the list before removing or disabling users, there are legitimate
+users who just read and never edit. If your wiki has strict ACLs, they might need
+to be able to log in to read. Use --show.
+
+Usage:
+    For all your wikis sharing a single user_dir, run:
+        moin ... account inactive --py-append keep-users.py
+    Then, run (for one of the wikis sharing this user_dir):
+        moin ... account inactive --py-exec keep-users.py --show
+    If you want to keep some user profiles that are shown there, add the userids to
+    the keep-users.py file in the same way as all the other userids you see there.
+    Finally run the command with --disable or --remove instead of --show.
+
+Detailed Instructions:
+======================
+General syntax: moin [options] account inactive [inactive-options]
+
+[options] usually should be:
+    --config-dir=/path/to/my/cfg/ --wiki-url=http://wiki.example.org/
+"""
+
+    def __init__(self, argv, def_values):
+        MoinScript.__init__(self, argv, def_values)
+        self.parser.add_option(
+            "--py-append", metavar="FILENAME", dest="py_append_file",
+            help="Append python code with editlog user ids."
+        )
+        self.parser.add_option(
+            "--py-exec", metavar="FILENAME", dest="py_exec_file",
+            help="Execute python code to read editlog user ids."
+        )
+        self.parser.add_option(
+            "--show", dest="show", action="store_true",
+            help="Show all inactive users."
+        )
+        self.parser.add_option(
+            "--disable", dest="disable", action="store_true",
+            help="Disable all inactive users."
+        )
+        self.parser.add_option(
+            "--remove", dest="remove", action="store_true",
+            help="Remove all inactive users."
+        )
+        self.parser.add_option(
+            "--interactive", dest="interactive", action="store_true",
+            help="Show data and ask before deleting/disabling users."
+        )
+
+    def mainloop(self):
+        argc = len(self.args)
+
+        self.init_request()
+        request = self.request
+
+        if self.options.py_append_file:
+            def logs(request):
+                """ generator for log objects """
+                # global edit-log
+                yield editlog.EditLog(request)
+                # local edit-log of every page
+                for pn in request.rootpage.getPageList(exists=False):
+                    yield editlog.EditLog(request, rootpagename=pn)
+
+            def uids(log):
+                """ generator for userids found in a log """
+                for line in log:
+                    uid = line.userid
+                    if uid:
+                        yield uid
+
+            fn = self.options.py_append_file
+            editlog_uids = set()
+            for log in logs(request):
+                editlog_uids |= set(uids(log))
+            with open(fn, "a") as f:
+                for uid in editlog_uids:
+                    u = User(request, uid)
+                    code = u'editlog_uids.add(%r)  # %r %r %r\n' % (
+                               uid, u.name, u.email, u.jid)
+                    f.write(code.encode('utf-8'))
+
+        elif self.options.py_exec_file:
+            def check_interactive(u):
+                if self.options.interactive:
+                    prompt = "%s %r %r %r disabled=%r (y/N)? " % (u.id, u.name, u.email, u.jid, u.disabled)
+                    return raw_input(prompt).strip() in ['y', 'Y', ]
+                else:
+                    return True
+
+            fn = self.options.py_exec_file
+            locs = dict(editlog_uids=set())
+            execfile(fn, {}, locs)
+            editlog_uids = locs.get('editlog_uids')
+
+            profile_uids = set(getUserList(request))
+
+            inactive_uids = profile_uids - editlog_uids
+            for uid in inactive_uids:
+                u = User(request, uid)
+                if self.options.show:
+                    print "%s\t%r\t%r\t%r" % (uid, u.name, u.email, u.disabled)
+                if self.options.disable:
+                    if check_interactive(u):
+                        u.disabled = 1
+                        u.save()
+                elif self.options.remove:
+                    if check_interactive(u):
+                        u.remove()
+
--- a/MoinMoin/script/account/resetpw.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/script/account/resetpw.py	Sun Mar 24 14:58:56 2013 +0100
@@ -1,13 +1,17 @@
 # -*- coding: iso-8859-1 -*-
 """
-MoinMoin - disable a user account
+MoinMoin - change or reset the password of a user account
 
-@copyright: 2006 MoinMoin:ThomasWaldmann,
+@copyright: 2006-2013 MoinMoin:ThomasWaldmann,
             2008 MoinMoin:JohannesBerg
 @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.script import MoinScript
+import sys
+
+from MoinMoin.script import MoinScript, log
+from MoinMoin.user import getUserList, set_password, Fault
+
 
 class PluginScript(MoinScript):
     """\
@@ -17,17 +21,33 @@
 
 Detailed Instructions:
 ======================
-General syntax: moin [options] account resetpw [newpw-options] newpassword
+General syntax: moin [options] account resetpw [newpw-options] [newpassword]
 
 [options] usually should be:
     --config-dir=/path/to/my/cfg/ --wiki-url=http://wiki.example.org/
 
+newpassword:
+    The new password, optional. If newpassword is not given, the password will
+    be invalidated (and the user will not be able to log in with any password,
+    so the user will need to do a password recovery).
+
 [newpw-options] see below:
     1. To change JohnSmith's password:
-       moin ... account resetpw --name JohnSmith new-password
+       moin ... account resetpw --name JohnSmith JohnsNewSuperSecretPassword
 
     2. To change the password for the UID '1198872910.78.56322':
-       moin ... account resetpw --uid 1198872910.78.56322 new-password
+       moin ... account resetpw --uid 1198872910.78.56322 TheNewPassword
+
+    3. To invalidate the password of all users and notify them via e-mail,
+       giving verbose progress information:
+       moin ... --verbose account resetpw --all-users --notify
+
+       Please note:
+       - if you have many users, this will generate many e-mails
+       - if a user does not have an e-mail address in his profile, he can not
+         get notified
+       - disabled user profiles will get the password reset, but won't get
+         notified (they can't be used any more anyway).
 """
 
     def __init__(self, argv, def_values):
@@ -40,31 +60,103 @@
             "--name", metavar="NAME", dest="uname",
             help="Reset password for the user with user name NAME."
         )
+        self.parser.add_option(
+            "-a", "--all-users", dest="all_users", action="store_true",
+            help="Reset password for ALL users."
+        )
+        self.parser.add_option(
+            "--notify", dest="notify", action="store_true",
+            help="Notify user(s), send them an E-Mail with a password reset link."
+        )
+        self.parser.add_option(
+            "--subject", metavar="SUBJECT", dest="subject",
+            help="Subject text for the password reset notification E-Mail."
+        )
+        self.parser.add_option(
+            "--text-intro", metavar="TEXT_INTRO", dest="text_intro",
+            help="Intro text for the password reset notification E-Mail. Default: empty."
+        )
+        self.parser.add_option(
+            "--text-msg", metavar="TEXT_MSG", dest="text_msg",
+            help="Main text for the password reset notification E-Mail. Default: use the builtin standard message"
+        )
+        self.parser.add_option(
+            "--text-data", metavar="TEXT_DATA", dest="text_data",
+            help="Data template text for the password reset notification E-Mail. Default: use the builtin standard data template"
+        )
+        self.parser.add_option(
+            "--text-from-file", metavar="TEXT_DATA", dest="text_file",
+            help="Read full template for the password reset notification E-Mail from the given file, overrides --text-intro/msg/data. Default: None"
+        )
+        self.parser.add_option(
+            "--skip-invalid", dest="skip_invalid", action="store_true",
+            help="If a user's password hash is already invalid (pw is already reset), skip this user."
+        )
+        self.parser.add_option(
+            "-v", "--verbose", dest="verbose", action="store_true",
+            help="Verbose operation."
+        )
 
     def mainloop(self):
-        # we don't expect non-option arguments
-        if len(self.args) != 1:
-            self.parser.error("no new password given")
-        newpass = self.args[0]
+        argc = len(self.args)
+        if argc < 1:
+            newpass = None
+        elif argc == 1:
+            newpass = self.args[0]
+        else:
+            self.parser.error("too many arguments given")
 
-        flags_given = self.options.uid or self.options.uname
+        flags_given = self.options.uid or self.options.uname or self.options.all_users
         if not flags_given:
             self.parser.print_help()
-            import sys
             sys.exit(1)
 
         self.init_request()
         request = self.request
 
-        from MoinMoin import user
-        if self.options.uid:
-            u = user.User(request, self.options.uid)
-        elif self.options.uname:
-            u = user.User(request, None, self.options.uname)
+        notify = self.options.notify
+        if notify and not request.cfg.mail_enabled:
+            print "This wiki is not enabled for mail processing. The --notify option requires this. Aborting..."
+            sys.exit(1)
 
-        if not u.exists():
-            print 'This user "%s" does not exists!' % u.name
-            return
+        skip_invalid = self.options.skip_invalid
+        subject = self.options.subject
+        text_intro = self.options.text_intro
+        text_msg = self.options.text_msg
+        text_data = self.options.text_data
+        text_file = self.options.text_file
 
-        u.enc_password = user.encodePassword(newpass)
-        u.save()
+        if text_file:
+            text_intro = ''
+            text_msg = ''
+            with open(text_file) as f:
+                text_data = f.read().decode('utf-8')
+
+        if self.options.uid:
+            try:
+                set_password(request, newpass, uid=self.options.uid,
+                             notify=notify, skip_invalid=skip_invalid,
+                             subject=subject,
+                             text_intro=text_intro, text_msg=text_msg, text_data=text_data)
+            except Fault, err:
+                print str(err)
+        elif self.options.uname:
+            try:
+                set_password(request, newpass, uname=self.options.uname,
+                             notify=notify, skip_invalid=skip_invalid,
+                             subject=subject,
+                             text_intro=text_intro, text_msg=text_msg, text_data=text_data)
+            except Fault, err:
+                print str(err)
+        elif self.options.all_users:
+            uids = sorted(getUserList(request))
+            total = len(uids)
+            for nr, uid in enumerate(uids, start=1):
+                log("%05d / %05d - processing uid %s" % (nr, total, uid))
+                try:
+                    set_password(request, newpass, uid=uid,
+                                 notify=notify, skip_invalid=skip_invalid,
+                                 subject=subject,
+                                 text_intro=text_intro, text_msg=text_msg, text_data=text_data)
+                except Fault, err:
+                    print str(err)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/export/eventlog.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,65 @@
+# -*- coding: iso-8859-1 -*-
+"""
+MoinMoin - Dump event-log to CSV
+
+@copyright: 2013 MoinMoin:ThomasWaldmann
+@license: GNU GPL, see COPYING for details.
+"""
+
+import sys
+import csv
+
+from MoinMoin import script
+from MoinMoin.logfile.eventlog import EventLog
+
+
+class PluginScript(script.MoinScript):
+    """\
+Purpose:
+========
+This tool allows you to dump a MoinMoin wiki event-log to CSV.
+
+Detailed Instructions:
+======================
+General syntax: moin [options] export eventlog [eventlog-options]
+
+[options] usually should be:
+    --config-dir=/path/to/my/cfg/ --wiki-url=http://wiki.example.org/
+
+[eventlog-options] see below:
+    To write into a file (default: stdout):
+    --file=filename.csv
+"""
+
+    def __init__(self, argv=None, def_values=None):
+        script.MoinScript.__init__(self, argv, def_values)
+        self.parser.add_option(
+            "-f", "--file", dest="csv_fname",
+            help="CSV output filename [default: stdout]"
+        )
+
+    def mainloop(self):
+        self.init_request()
+        request = self.request
+
+        if self.options.csv_fname:
+            csv_file = open(self.options.csv_fname, "w")
+        else:
+            csv_file = sys.stdout
+
+        columns = ['time', 'event', 'username', 'ip', 'wikiname', 'pagename', 'url', 'referrer', 'ua', ]
+        csv_out = csv.DictWriter(csv_file, columns, restval='', extrasaction='ignore')
+        for time, event, kv in EventLog(request):
+            kv = kv.to_dict()  # convert from MultiDict to dict
+            # convert usecs to secs
+            time = time / 1000000.0
+            # change some key names to simpler ones:
+            ip = kv.pop('REMOTE_ADDR', '')
+            ua = kv.pop('HTTP_USER_AGENT', '')
+            referrer = kv.pop('HTTP_REFERER', '')
+            kv.update(dict(time=unicode(time), event=event, ip=ip, referrer=referrer, ua=ua))
+            # csv can't handle unicode, encode to utf-8:
+            kv = dict([(k, v.encode('utf-8')) for k, v in kv.iteritems()])
+            csv_out.writerow(kv)
+        csv_file.close()
+
--- a/MoinMoin/script/import/wikipage.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/script/import/wikipage.py	Sun Mar 24 14:58:56 2013 +0100
@@ -2,13 +2,17 @@
 """
 MoinMoin - import wiki pages from local files into the wiki.
 
-@copyright: 2010 MoinMoin:PascalVolk
+@copyright: 2010 MoinMoin:PascalVolk,
+            2013 MoinMoin:ReimarBauer
+
 @license: GNU GPL, see COPYING for details.
 """
+import calendar
+import time
 
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin.script import MoinScript, fatal, log
-from MoinMoin.wikiutil import clean_input, decodeUnknownInput
+from MoinMoin.wikiutil import clean_input, decodeUnknownInput, timestamp2version
 
 
 class IAmRoot(object):
@@ -36,6 +40,9 @@
         self.parser.add_option('--author', dest='author', metavar='AUTHOR',
                 default='PageImporter',
                 help='Use AUTHOR for edit history / RecentChanges')
+        self.parser.add_option('--mtime', dest='mtime', metavar='mtime',
+                default=None,
+                help='Use TIME (YYYY-MM-DD HH:MM:SS) in UTC for edit history / RecentChanges. Default value is the current UTC time')
         self.parser.add_option('--comment', dest='comment', metavar='COMMENT',
                 default='', help='COMMENT for edit history / RecentChanges')
         self.parser.add_option('--file', dest='file', default='',
@@ -69,8 +76,14 @@
             acl = '#acl %s\n' % self.options.acl
         comment = clean_input(self.options.comment)
 
+        if self.options.mtime:
+            mtime = timestamp2version(calendar.timegm(time.strptime(self.options.mtime, "%Y-%m-%d %H:%M:%S")))
+        else:
+            mtime = timestamp2version(time.time())
+
+
         pe = PageEditor(request, self.options.page, do_editor_backup=0,
-                        uid_override=self.options.author,
+                        uid_override=self.options.author, mtime=mtime,
                         do_revision_backup=int(self.options.revision_backup))
         try:
             pe.saveText(acl + page_content, 0, comment=comment)
--- a/MoinMoin/script/maint/cleancache.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/script/maint/cleancache.py	Sun Mar 24 14:58:56 2013 +0100
@@ -71,6 +71,7 @@
             caching.CacheEntry(request, 'drafts', key, scope='wiki').remove()
 
         # clean language cache files
+        caching.CacheEntry(request, 'i18n', 'meta', scope='wiki').remove()
         wiki_languages = i18n.wikiLanguages().keys()
         for key in wiki_languages:
             caching.CacheEntry(request, 'i18n', key, scope='wiki').remove()
--- a/MoinMoin/script/migration/1090500.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/script/migration/1090500.py	Sun Mar 24 14:58:56 2013 +0100
@@ -1,13 +1,13 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - dummy migration terminator script
+    MoinMoin - migration from base rev 1090500
 
-    This must be the last migration script.
+    Nothing to do, we just return the new data dir revision.
 
-    @copyright: 2008 by Thomas Waldmann
+    @copyright: 2012 by Thomas Waldmann
     @license: GNU GPL, see COPYING for details.
 """
 
 def execute(script, data_dir, rev):
-    return None
+    return 1090600
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/1090600.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,13 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - migration from base rev 1090600
+
+    Nothing to do, we just return the new data dir revision.
+
+    @copyright: 2013 by Thomas Waldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+def execute(script, data_dir, rev):
+    return 1090700
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/migration/1090700.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,13 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - dummy migration terminator script
+
+    This must be the last migration script.
+
+    @copyright: 2008 by Thomas Waldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+def execute(script, data_dir, rev):
+    return None
+
--- a/MoinMoin/security/textcha.py	Sat Sep 22 22:48:40 2012 +0200
+++ b/MoinMoin/security/textcha.py	Sun Mar 24 14:58:56 2013 +0100
@@ -29,6 +29,7 @@
 logging = log.getLogger(__name__)
 
 from MoinMoin import wikiutil
+from werkzeug.security import safe_str_cmp as safe_str_equal
 from MoinMoin.support.python_compatibility import hmac_new
 
 SHA1_LEN = 40 # length of hexdigest
@@ -137,7 +138,7 @@
             if not timestamp or timestamp + self.expiry_time < time():
                 success = False
             try:
-                if self._compute_signature(self.question, timestamp) != signature:
+                if not safe_str_equal(self._compute_signature(self.question, timestamp), signature):
                     success = False
             except TypeError:
                 success = False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/passlib/__init__.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,3 @@
+"""passlib - suite of password hashing & generation routinges"""
+
+__version__ = '1.6.1'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/passlib/_setup/__init__.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,1 @@
+"""passlib.setup - helpers used by passlib's setup.py script"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/passlib/_setup/docdist.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,87 @@
+"custom command to build doc.zip file"
+#=============================================================================
+# imports
+#=============================================================================
+# core
+import os
+from distutils import dir_util
+from distutils.cmd import Command
+from distutils.errors import *
+from distutils.spawn import spawn
+# local
+__all__ = [
+    "docdist"
+]
+#=============================================================================
+# command
+#=============================================================================
+class docdist(Command):
+
+    description = "create zip file containing standalone html docs"
+
+    user_options = [
+        ('build-dir=', None, 'Build directory'),
+        ('dist-dir=', 'd',
+         "directory to put the source distribution archive(s) in "
+         "[default: dist]"),
+        ('format=', 'f',
+         "archive format to create (tar, ztar, gztar, zip)"),
+        ('sign', 's', 'sign files using gpg'),
+        ('identity=', 'i', 'GPG identity used to sign files'),
+    ]
+
+    def initialize_options(self):
+        self.build_dir = None
+        self.dist_dir = None
+        self.format = None
+        self.keep_temp = False
+        self.sign = False
+        self.identity = None
+
+    def finalize_options(self):
+        if self.identity and not self.sign:
+            raise DistutilsOptionError(
+                "Must use --sign for --identity to have meaning"
+            )
+        if self.build_dir is None:
+            cmd = self.get_finalized_command('build')
+            self.build_dir = os.path.join(cmd.build_base, 'docdist')
+        if not self.dist_dir:
+            self.dist_dir = "dist"
+        if not self.format:
+            self.format = "zip"
+
+    def run(self):
+        # call build sphinx to build docs
+        self.run_command("build_sphinx")
+        cmd = self.get_finalized_command("build_sphinx")
+        source_dir = cmd.builder_target_dir
+
+        # copy to directory with appropriate name
+        dist = self.distribution
+        arc_name = "%s-docs-%s" % (dist.get_name(), dist.get_version())
+        tmp_dir = os.path.join(self.build_dir, arc_name)
+        if os.path.exists(tmp_dir):
+            dir_util.remove_tree(tmp_dir, dry_run=self.dry_run)
+        self.copy_tree(source_dir, tmp_dir, preserve_symlinks=True)
+
+        # make archive from dir
+        arc_base = os.path.join(self.dist_dir, arc_name)
+        self.arc_filename = self.make_archive(arc_base, self.format,
+                                              self.build_dir)
+
+        # Sign if requested
+        if self.sign:
+            gpg_args = ["gpg", "--detach-sign", "-a", self.arc_filename]
+            if self.identity:
+                gpg_args[2:2] = ["--local-user", self.identity]
+            spawn(gpg_args,
+                  dry_run=self.dry_run)
+
+        # cleanup
+        if not self.keep_temp:
+            dir_util.remove_tree(tmp_dir, dry_run=self.dry_run)
+
+#=============================================================================
+# eof
+#=============================================================================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/passlib/_setup/stamp.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,57 @@
+"update version string during build"
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import with_statement
+# core
+import os
+import re
+import time
+from distutils.dist import Distribution
+# pkg
+# local
+__all__ = [
+    "stamp_source",
+    "stamp_distutils_output",
+]
+#=============================================================================
+# helpers
+#=============================================================================
+def get_command_class(opts, name):
+    return opts['cmdclass'].get(name) or Distribution().get_command_class(name)
+
+def stamp_source(base_dir, version, dry_run=False):
+    "update version string in passlib dist"
+    path = os.path.join(base_dir, "passlib", "__init__.py")
+    with open(path) as fh:
+        input = fh.read()
+    output, count = re.subn('(?m)^__version__\s*=.*$',
+                    '__version__ = ' + repr(version),
+                    input)
+    assert count == 1, "failed to replace version string"
+    if not dry_run:
+        os.unlink(path) # sdist likes to use hardlinks
+        with open(path, "w") as fh:
+            fh.write(output)
+
+def stamp_distutils_output(opts, version):
+
+    # subclass buildpy to update version string in source
+    _build_py = get_command_class(opts, "build_py")
+    class build_py(_build_py):
+        def build_packages(self):
+            _build_py.build_packages(self)
+            stamp_source(self.build_lib, version, self.dry_run)
+    opts['cmdclass']['build_py'] = build_py
+
+    # subclass sdist to do same thing
+    _sdist = get_command_class(opts, "sdist")
+    class sdist(_sdist):
+        def make_release_tree(self, base_dir, files):
+            _sdist.make_release_tree(self, base_dir, files)
+            stamp_source(base_dir, version, self.dry_run)
+    opts['cmdclass']['sdist'] = sdist
+
+#=============================================================================
+# eof
+#=============================================================================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/passlib/apache.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,1037 @@
+"""passlib.apache - apache password support"""
+# XXX: relocate this to passlib.ext.apache?
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import with_statement
+# core
+from hashlib import md5
+import logging; log = logging.getLogger(__name__)
+import os
+import sys
+from warnings import warn
+# site
+# pkg
+from passlib.context import CryptContext
+from passlib.exc import ExpectedStringError
+from passlib.hash import htdigest
+from passlib.utils import consteq, render_bytes, to_bytes, deprecated_method, is_ascii_codec
+from passlib.utils.compat import b, bytes, join_bytes, str_to_bascii, u, \
+                                 unicode, BytesIO, iteritems, imap, PY3
+# local
+__all__ = [
+    'HtpasswdFile',
+    'HtdigestFile',
+]
+
+#=============================================================================
+# constants & support
+#=============================================================================
+_UNSET = object()
+
+_BCOLON = b(":")
+
+# byte values that aren't allowed in fields.
+_INVALID_FIELD_CHARS = b(":\n\r\t\x00")
+
+#=============================================================================
+# backport of OrderedDict for PY2.5
+#=============================================================================
+try:
+    from collections import OrderedDict
+except ImportError:
+    # Python 2.5
+    class OrderedDict(dict):
+        """hacked OrderedDict replacement.
+
+        NOTE: this doesn't provide a full OrderedDict implementation,
+        just the minimum needed by the Htpasswd internals.
+        """
+        def __init__(self):
+            self._keys = []
+
+        def __iter__(self):
+            return iter(self._keys)
+
+        def __setitem__(self, key, value):
+            if key not in self:
+                self._keys.append(key)
+            super(OrderedDict, self).__setitem__(key, value)
+
+        def __delitem__(self, key):
+            super(OrderedDict, self).__delitem__(key)
+            self._keys.remove(key)
+
+        def iteritems(self):
+            return ((key, self[key]) for key in self)
+
+        # these aren't used or implemented, so disabling them for safety.
+        update = pop = popitem = clear = keys = iterkeys = None
+
+#=============================================================================
+# common helpers
+#=============================================================================
+class _CommonFile(object):
+    """common framework for HtpasswdFile & HtdigestFile"""
+    #===================================================================
+    # instance attrs
+    #===================================================================
+
+    # charset encoding used by file (defaults to utf-8)
+    encoding = None
+
+    # whether users() and other public methods should return unicode or bytes?
+    # (defaults to False under PY2, True under PY3)
+    return_unicode = None
+
+    # if bound to local file, these will be set.
+    _path = None # local file path
+    _mtime = None # mtime when last loaded, or 0
+
+    # if true, automatically save to local file after changes are made.
+    autosave = False
+
+    # ordered dict mapping key -> value for all records in database.
+    # (e.g. user => hash for Htpasswd)
+    _records = None
+
+    #===================================================================
+    # alt constuctors
+    #===================================================================
+    @classmethod
+    def from_string(cls, data, **kwds):
+        """create new object from raw string.
+
+        :type data: unicode or bytes
+        :arg data:
+            database to load, as single string.
+
+        :param \*\*kwds:
+            all other keywords are the same as in the class constructor
+        """
+        if 'path' in kwds:
+            raise TypeError("'path' not accepted by from_string()")
+        self = cls(**kwds)
+        self.load_string(data)
+        return self
+
+    @classmethod
+    def from_path(cls, path, **kwds):
+        """create new object from file, without binding object to file.
+
+        :type path: str
+        :arg path:
+            local filepath to load from
+
+        :param \*\*kwds:
+            all other keywords are the same as in the class constructor
+        """
+        self = cls(**kwds)
+        self.load(path)
+        return self
+
+    #===================================================================
+    # init
+    #===================================================================
+    def __init__(self, path=None, new=False, autoload=True, autosave=False,
+                 encoding="utf-8", return_unicode=PY3,
+                 ):
+        # set encoding
+        if not encoding:
+            warn("``encoding=None`` is deprecated as of Passlib 1.6, "
+                 "and will cause a ValueError in Passlib 1.8, "
+                 "use ``return_unicode=False`` instead.",
+                 DeprecationWarning, stacklevel=2)
+            encoding = "utf-8"
+            return_unicode = False
+        elif not is_ascii_codec(encoding):
+            # htpasswd/htdigest files assumes 1-byte chars, and use ":" separator,
+            # so only ascii-compatible encodings are allowed.
+            raise ValueError("encoding must be 7-bit ascii compatible")
+        self.encoding = encoding
+
+        # set other attrs
+        self.return_unicode = return_unicode
+        self.autosave = autosave
+        self._path = path
+        self._mtime = 0
+
+        # init db
+        if not autoload:
+            warn("``autoload=False`` is deprecated as of Passlib 1.6, "
+                 "and will be removed in Passlib 1.8, use ``new=True`` instead",
+                 DeprecationWarning, stacklevel=2)
+            new = True
+        if path and not new:
+            self.load()
+        else:
+            self._records = OrderedDict()
+
+    def __repr__(self):
+        tail = ''
+        if self.autosave:
+            tail += ' autosave=True'
+        if self._path:
+            tail += ' path=%r' % self._path
+        if self.encoding != "utf-8":
+            tail += ' encoding=%r' % self.encoding
+        return "<%s 0x%0x%s>" % (self.__class__.__name__, id(self), tail)
+
+    # NOTE: ``path`` is a property so that ``_mtime`` is wiped when it's set.
+    def _get_path(self):
+        return self._path
+    def _set_path(self, value):
+        if value != self._path:
+            self._mtime = 0
+        self._path = value
+    path = property(_get_path, _set_path)
+
+    @property
+    def mtime(self):
+        "modify time when last loaded (if bound to a local file)"
+        return self._mtime
+
+    #===================================================================
+    # loading
+    #===================================================================
+    def load_if_changed(self):
+        """Reload from ``self.path`` only if file has changed since last load"""
+        if not self._path:
+            raise RuntimeError("%r is not bound to a local file" % self)
+        if self._mtime and self._mtime == os.path.getmtime(self._path):
+            return False
+        self.load()
+        return True
+
+    def load(self, path=None, force=True):
+        """Load state from local file.
+        If no path is specified, attempts to load from ``self.path``.
+
+        :type path: str
+        :arg path: local file to load from
+
+        :type force: bool
+        :param force:
+            if ``force=False``, only load from ``self.path`` if file
+            has changed since last load.
+
+            .. deprecated:: 1.6
+                This keyword will be removed in Passlib 1.8;
+                Applications should use :meth:`load_if_changed` instead.
+        """
+        if path is not None:
+            with open(path, "rb") as fh:
+                self._mtime = 0
+                self._load_lines(fh)
+        elif not force:
+            warn("%(name)s.load(force=False) is deprecated as of Passlib 1.6,"
+                 "and will be removed in Passlib 1.8; "
+                 "use %(name)s.load_if_changed() instead." %
+                 dict(name=self.__class__.__name__),
+                 DeprecationWarning, stacklevel=2)
+            return self.load_if_changed()
+        elif self._path:
+            with open(self._path, "rb") as fh:
+                self._mtime = os.path.getmtime(self._path)
+                self._load_lines(fh)
+        else:
+            raise RuntimeError("%s().path is not set, an explicit path is required" %
+                               self.__class__.__name__)
+        return True
+
+    def load_string(self, data):
+        "Load state from unicode or bytes string, replacing current state"
+        data = to_bytes(data, self.encoding, "data")
+        self._mtime = 0
+        self._load_lines(BytesIO(data))
+
+    def _load_lines(self, lines):
+        "load from sequence of lists"
+        # XXX: found reference that "#" comment lines may be supported by
+        #      htpasswd, should verify this, and figure out how to handle them.
+        #      if true, this would also affect what can be stored in user field.
+        # XXX: if multiple entries for a key, should we use the first one
+        #      or the last one? going w/ first entry for now.
+        # XXX: how should this behave if parsing fails? currently
+        #      it will contain everything that was loaded up to error.
+        #      could clear / restore old state instead.
+        parse = self._parse_record
+        records = self._records = OrderedDict()
+        for idx, line in enumerate(lines):
+            key, value = parse(line, idx+1)
+            if key not in records:
+                records[key] = value
+
+    def _parse_record(cls, record, lineno): # pragma: no cover - abstract method
+        "parse line of file into (key, value) pair"
+        raise NotImplementedError("should be implemented in subclass")
+
+    #===================================================================
+    # saving
+    #===================================================================
+    def _autosave(self):
+        "subclass helper to call save() after any changes"
+        if self.autosave and self._path:
+            self.save()
+
+    def save(self, path=None):
+        """Save current state to file.
+        If no path is specified, attempts to save to ``self.path``.
+        """
+        if path is not None:
+            with open(path, "wb") as fh:
+                fh.writelines(self._iter_lines())
+        elif self._path:
+            self.save(self._path)
+            self._mtime = os.path.getmtime(self._path)
+        else:
+            raise RuntimeError("%s().path is not set, cannot autosave" %
+                               self.__class__.__name__)
+
+    def to_string(self):
+        "Export current state as a string of bytes"
+        return join_bytes(self._iter_lines())
+
+    def _iter_lines(self):
+        "iterator yielding lines of database"
+        return (self._render_record(key,value) for key,value in iteritems(self._records))
+
+    def _render_record(cls, key, value): # pragma: no cover - abstract method
+        "given key/value pair, encode as line of file"
+        raise NotImplementedError("should be implemented in subclass")
+
+    #===================================================================
+    # field encoding
+    #===================================================================
+    def _encode_user(self, user):
+        "user-specific wrapper for _encode_field()"
+        return self._encode_field(user, "user")
+
+    def _encode_realm(self, realm): # pragma: no cover - abstract method
+        "realm-specific wrapper for _encode_field()"
+        return self._encode_field(realm, "realm")
+
+    def _encode_field(self, value, param="field"):
+        """convert field to internal representation.
+
+        internal representation is always bytes. byte strings are left as-is,
+        unicode strings encoding using file's default encoding (or ``utf-8``
+        if no encoding has been specified).
+
+        :raises UnicodeEncodeError:
+            if unicode value cannot be encoded using default encoding.
+
+        :raises ValueError:
+            if resulting byte string contains a forbidden character,
+            or is too long (>255 bytes).
+
+        :returns:
+            encoded identifer as bytes
+        """
+        if isinstance(value, unicode):
+            value = value.encode(self.encoding)
+        elif not isinstance(value, bytes):
+            raise ExpectedStringError(value, param)
+        if len(value) > 255:
+            raise ValueError("%s must be at most 255 characters: %r" %
+                             (param, value))
+        if any(c in _INVALID_FIELD_CHARS for c in value):
+            raise ValueError("%s contains invalid characters: %r" %
+                             (param, value,))
+        return value
+
+    def _decode_field(self, value):
+        """decode field from internal representation to format
+        returns by users() method, etc.
+
+        :raises UnicodeDecodeError:
+            if unicode value cannot be decoded using default encoding.
+            (usually indicates wrong encoding set for file).
+
+        :returns:
+            field as unicode or bytes, as appropriate.
+        """
+        assert isinstance(value, bytes), "expected value to be bytes"
+        if self.return_unicode:
+            return value.decode(self.encoding)
+        else:
+            return value
+
+    # FIXME: htpasswd doc says passwords limited to 255 chars under Windows & MPE,
+    # and that longer ones are truncated. this may be side-effect of those
+    # platforms supporting the 'plaintext' scheme. these classes don't currently
+    # check for this.
+
+    #===================================================================
+    # eoc
+    #===================================================================
+
+#=============================================================================
+# htpasswd editing
+#=============================================================================
+
+# FIXME: apr_md5_crypt technically the default only for windows, netware and tpf.
+# TODO: find out if htpasswd's "crypt" mode is a crypt() *call* or just des_crypt implementation.
+#       if the former, we can support anything supported by passlib.hosts.host_context,
+#       allowing more secure hashes than apr_md5_crypt to be used.
+#       could perhaps add this behavior as an option to the constructor.
+#       c.f. http://httpd.apache.org/docs/2.2/programs/htpasswd.html
+htpasswd_context = CryptContext([
+    "apr_md5_crypt", # man page notes supported everywhere, default on Windows, Netware, TPF
+    "des_crypt", # man page notes server does NOT support this on Windows, Netware, TPF
+    "ldap_sha1", # man page notes only for transitioning <-> ldap
+    "plaintext" # man page notes server ONLY supports this on Windows, Netware, TPF
+    ])
+
+class HtpasswdFile(_CommonFile):
+    """class for reading & writing Htpasswd files.
+
+    The class constructor accepts the following arguments:
+
+    :type path: filepath
+    :param path:
+
+        Specifies path to htpasswd file, use to implicitly load from and save to.
+
+        This class has two modes of operation:
+
+        1. It can be "bound" to a local file by passing a ``path`` to the class
+           constructor. In this case it will load the contents of the file when
+           created, and the :meth:`load` and :meth:`save` methods will automatically
+           load from and save to that file if they are called without arguments.
+
+        2. Alternately, it can exist as an independant object, in which case
+           :meth:`load` and :meth:`save` will require an explicit path to be
+           provided whenever they are called. As well, ``autosave`` behavior
+           will not be available.
+
+           This feature is new in Passlib 1.6, and is the default if no
+           ``path`` value is provided to the constructor.
+
+        This is also exposed as a readonly instance attribute.
+
+    :type new: bool
+    :param new:
+
+        Normally, if *path* is specified, :class:`HtpasswdFile` will
+        immediately load the contents of the file. However, when creating
+        a new htpasswd file, applications can set ``new=True`` so that
+        the existing file (if any) will not be loaded.
+
+        .. versionadded:: 1.6
+            This feature was previously enabled by setting ``autoload=False``.
+            That alias has been deprecated, and will be removed in Passlib 1.8
+
+    :type autosave: bool
+    :param autosave:
+
+        Normally, any changes made to an :class:`HtpasswdFile` instance
+        will not be saved until :meth:`save` is explicitly called. However,
+        if ``autosave=True`` is specified, any changes made will be
+        saved to disk immediately (assuming *path* has been set).
+
+        This is also exposed as a writeable instance attribute.
+
+    :type encoding: str
+    :param encoding:
+
+        Optionally specify character encoding used to read/write file
+        and hash passwords. Defaults to ``utf-8``, though ``latin-1``
+        is the only other commonly encountered encoding.
+
+        This is also exposed as a readonly instance attribute.
+
+    :type default_scheme: str
+    :param default_scheme:
+        Optionally specify default scheme to use when encoding new passwords.
+        Must be one of ``"apr_md5_crypt"``, ``"des_crypt"``, ``"ldap_sha1"``,
+        ``"plaintext"``. It defaults to ``"apr_md5_crypt"``.
+
+        .. versionadded:: 1.6
+            This keyword was previously named ``default``. That alias
+            has been deprecated, and will be removed in Passlib 1.8.
+
+    :type context: :class:`~passlib.context.CryptContext`
+    :param context:
+        :class:`!CryptContext` instance used to encrypt
+        and verify the hashes found in the htpasswd file.
+        The default value is a pre-built context which supports all
+        of the hashes officially allowed in an htpasswd file.
+
+        This is also exposed as a readonly instance attribute.
+
+        .. warning::
+
+            This option may be used to add support for non-standard hash
+            formats to an htpasswd file. However, the resulting file
+            will probably not be usuable by another application,
+            and particularly not by Apache.
+
+    :param autoload:
+        Set to ``False`` to prevent the constructor from automatically
+        loaded the file from disk.
+
+        .. deprecated:: 1.6
+            This has been replaced by the *new* keyword.
+            Instead of setting ``autoload=False``, you should use
+            ``new=True``. Support for this keyword will be removed
+            in Passlib 1.8.
+
+    :param default:
+        Change the default algorithm used to encrypt new passwords.
+
+        .. deprecated:: 1.6
+            This has been renamed to *default_scheme* for clarity.
+            Support for this alias will be removed in Passlib 1.8.
+
+    Loading & Saving
+    ================
+    .. automethod:: load
+    .. automethod:: load_if_changed
+    .. automethod:: load_string
+    .. automethod:: save
+    .. automethod:: to_string
+
+    Inspection
+    ================
+    .. automethod:: users
+    .. automethod:: check_password
+    .. automethod:: get_hash
+
+    Modification
+    ================
+    .. automethod:: set_password
+    .. automethod:: delete
+
+    Alternate Constructors
+    ======================
+    .. automethod:: from_string
+
+    Attributes
+    ==========
+    .. attribute:: path
+
+        Path to local file that will be used as the default
+        for all :meth:`load` and :meth:`save` operations.
+        May be written to, initialized by the *path* constructor keyword.
+
+    .. attribute:: autosave
+
+        Writeable flag indicating whether changes will be automatically
+        written to *path*.
+
+    Errors
+    ======
+    :raises ValueError:
+        All of the methods in this class will raise a :exc:`ValueError` if
+        any user name contains a forbidden character (one of ``:\\r\\n\\t\\x00``),
+        or is longer than 255 characters.
+    """
+    #===================================================================
+    # instance attrs
+    #===================================================================
+
+    # NOTE: _records map stores <user> for the key, and <hash> for the value,
+    # both in bytes which use self.encoding
+
+    #===================================================================
+    # init & serialization
+    #===================================================================
+    def __init__(self, path=None, default_scheme=None, context=htpasswd_context,
+                 **kwds):
+        if 'default' in kwds:
+            warn("``default`` is deprecated as of Passlib 1.6, "
+                 "and will be removed in Passlib 1.8, it has been renamed "
+                 "to ``default_scheem``.",
+                 DeprecationWarning, stacklevel=2)
+            default_scheme = kwds.pop("default")
+        if default_scheme:
+            context = context.copy(default=default_scheme)
+        self.context = context
+        super(HtpasswdFile, self).__init__(path, **kwds)
+
+    def _parse_record(self, record, lineno):
+        # NOTE: should return (user, hash) tuple
+        result = record.rstrip().split(_BCOLON)
+        if len(result) != 2:
+            raise ValueError("malformed htpasswd file (error reading line %d)"
+                             % lineno)
+        return result
+
+    def _render_record(self, user, hash):
+        return render_bytes("%s:%s\n", user, hash)
+
+    #===================================================================
+    # public methods
+    #===================================================================
+
+    def users(self):
+        "Return list of all users in database"
+        return [self._decode_field(user) for user in self._records]
+
+    ##def has_user(self, user):
+    ##    "check whether entry is present for user"
+    ##    return self._encode_user(user) in self._records
+
+    ##def rename(self, old, new):
+    ##    """rename user account"""
+    ##    old = self._encode_user(old)
+    ##    new = self._encode_user(new)
+    ##    hash = self._records.pop(old)
+    ##    self._records[new] = hash
+    ##    self._autosave()
+
+    def set_password(self, user, password):
+        """Set password for user; adds user if needed.
+
+        :returns:
+            * ``True`` if existing user was updated.
+            * ``False`` if user account was added.
+
+        .. versionchanged:: 1.6
+            This method was previously called ``update``, it was renamed
+            to prevent ambiguity with the dictionary method.
+            The old alias is deprecated, and will be removed in Passlib 1.8.
+        """
+        user = self._encode_user(user)
+        hash = self.context.encrypt(password)
+        if PY3:
+            hash = hash.encode(self.encoding)
+        existing = (user in self._records)
+        self._records[user] = hash
+        self._autosave()
+        return existing
+
+    @deprecated_method(deprecated="1.6", removed="1.8",
+                       replacement="set_password")
+    def update(self, user, password):
+        "set password for user"
+        return self.set_password(user, password)
+
+    def get_hash(self, user):
+        """Return hash stored for user, or ``None`` if user not found.
+
+        .. versionchanged:: 1.6
+            This method was previously named ``find``, it was renamed
+            for clarity. The old name is deprecated, and will be removed
+            in Passlib 1.8.
+        """
+        try:
+            return self._records[self._encode_user(user)]
+        except KeyError:
+            return None
+
+    @deprecated_method(deprecated="1.6", removed="1.8",
+                       replacement="get_hash")
+    def find(self, user):
+        "return hash for user"
+        return self.get_hash(user)
+
+    # XXX: rename to something more explicit, like delete_user()?
+    def delete(self, user):
+        """Delete user's entry.
+
+        :returns:
+            * ``True`` if user deleted.
+            * ``False`` if user not found.
+        """
+        try:
+            del self._records[self._encode_user(user)]
+        except KeyError:
+            return False
+        self._autosave()
+        return True
+
+    def check_password(self, user, password):
+        """Verify password for specified user.
+
+        :returns:
+            * ``None`` if user not found.
+            * ``False`` if user found, but password does not match.
+            * ``True`` if user found and password matches.
+
+        .. versionchanged:: 1.6
+            This method was previously called ``verify``, it was renamed
+            to prevent ambiguity with the :class:`!CryptContext` method.
+            The old alias is deprecated, and will be removed in Passlib 1.8.
+        """
+        user = self._encode_user(user)
+        hash = self._records.get(user)
+        if hash is None:
+            return None
+        if isinstance(password, unicode):
+            # NOTE: encoding password to match file, making the assumption
+            # that server will use same encoding to hash the password.
+            password = password.encode(self.encoding)
+        ok, new_hash = self.context.verify_and_update(password, hash)
+        if ok and new_hash is not None:
+            # rehash user's password if old hash was deprecated
+            self._records[user] = new_hash
+            self._autosave()
+        return ok
+
+    @deprecated_method(deprecated="1.6", removed="1.8",
+                       replacement="check_password")
+    def verify(self, user, password):
+        "verify password for user"
+        return self.check_password(user, password)
+
+    #===================================================================
+    # eoc
+    #===================================================================
+
+#=============================================================================
+# htdigest editing
+#=============================================================================
+class HtdigestFile(_CommonFile):
+    """class for reading & writing Htdigest files.
+
+    The class constructor accepts the following arguments:
+
+    :type path: filepath
+    :param path:
+
+        Specifies path to htdigest file, use to implicitly load from and save to.
+
+        This class has two modes of operation:
+
+        1. It can be "bound" to a local file by passing a ``path`` to the class
+           constructor. In this case it will load the contents of the file when
+           created, and the :meth:`load` and :meth:`save` methods will automatically
+           load from and save to that file if they are called without arguments.
+
+        2. Alternately, it can exist as an independant object, in which case
+           :meth:`load` and :meth:`save` will require an explicit path to be
+           provided whenever they are called. As well, ``autosave`` behavior
+           will not be available.
+
+           This feature is new in Passlib 1.6, and is the default if no
+           ``path`` value is provided to the constructor.
+
+        This is also exposed as a readonly instance attribute.
+
+    :type default_realm: str
+    :param default_realm:
+
+        If ``default_realm`` is set, all the :class:`HtdigestFile`
+        methods that require a realm will use this value if one is not
+        provided explicitly. If unset, they will raise an error stating
+        that an explicit realm is required.
+
+        This is also exposed as a writeable instance attribute.
+
+        .. versionadded:: 1.6
+
+    :type new: bool
+    :param new:
+
+        Normally, if *path* is specified, :class:`HtdigestFile` will
+        immediately load the contents of the file. However, when creating
+        a new htpasswd file, applications can set ``new=True`` so that
+        the existing file (if any) will not be loaded.
+
+        .. versionadded:: 1.6
+            This feature was previously enabled by setting ``autoload=False``.
+            That alias has been deprecated, and will be removed in Passlib 1.8
+
+    :type autosave: bool
+    :param autosave:
+
+        Normally, any changes made to an :class:`HtdigestFile` instance
+        will not be saved until :meth:`save` is explicitly called. However,
+        if ``autosave=True`` is specified, any changes made will be
+        saved to disk immediately (assuming *path* has been set).
+
+        This is also exposed as a writeable instance attribute.
+
+    :type encoding: str
+    :param encoding:
+
+        Optionally specify character encoding used to read/write file
+        and hash passwords. Defaults to ``utf-8``, though ``latin-1``
+        is the only other commonly encountered encoding.
+
+        This is also exposed as a readonly instance attribute.
+
+    :param autoload:
+        Set to ``False`` to prevent the constructor from automatically
+        loaded the file from disk.
+
+        .. deprecated:: 1.6
+            This has been replaced by the *new* keyword.
+            Instead of setting ``autoload=False``, you should use
+            ``new=True``. Support for this keyword will be removed
+            in Passlib 1.8.
+
+    Loading & Saving
+    ================
+    .. automethod:: load
+    .. automethod:: load_if_changed
+    .. automethod:: load_string
+    .. automethod:: save
+    .. automethod:: to_string
+
+    Inspection
+    ==========
+    .. automethod:: realms
+    .. automethod:: users
+    .. automethod:: check_password(user[, realm], password)
+    .. automethod:: get_hash
+
+    Modification
+    ============
+    .. automethod:: set_password(user[, realm], password)
+    .. automethod:: delete
+    .. automethod:: delete_realm
+
+    Alternate Constructors
+    ======================
+    .. automethod:: from_string
+
+    Attributes
+    ==========
+    .. attribute:: default_realm
+
+        The default realm that will be used if one is not provided
+        to methods that require it. By default this is ``None``,
+        in which case an explicit realm must be provided for every
+        method call. Can be written to.
+
+    .. attribute:: path
+
+        Path to local file that will be used as the default
+        for all :meth:`load` and :meth:`save` operations.
+        May be written to, initialized by the *path* constructor keyword.
+
+    .. attribute:: autosave
+
+        Writeable flag indicating whether changes will be automatically
+        written to *path*.
+
+    Errors
+    ======
+    :raises ValueError:
+        All of the methods in this class will raise a :exc:`ValueError` if
+        any user name or realm contains a forbidden character (one of ``:\\r\\n\\t\\x00``),
+        or is longer than 255 characters.
+    """
+    #===================================================================
+    # instance attrs
+    #===================================================================
+
+    # NOTE: _records map stores (<user>,<realm>) for the key,
+    # and <hash> as the value, all as <self.encoding> bytes.
+
+    # NOTE: unlike htpasswd, this class doesn't use a CryptContext,
+    # as only one hash format is supported: htdigest.
+
+    # optionally specify default realm that will be used if none
+    # is provided to a method call. otherwise realm is always required.
+    default_realm = None
+
+    #===================================================================
+    # init & serialization
+    #===================================================================
+    def __init__(self, path=None, default_realm=None, **kwds):
+        self.default_realm = default_realm
+        super(HtdigestFile, self).__init__(path, **kwds)
+
+    def _parse_record(self, record, lineno):
+        result = record.rstrip().split(_BCOLON)
+        if len(result) != 3:
+            raise ValueError("malformed htdigest file (error reading line %d)"
+                             % lineno)
+        user, realm, hash = result
+        return (user, realm), hash
+
+    def _render_record(self, key, hash):
+        user, realm = key
+        return render_bytes("%s:%s:%s\n", user, realm, hash)
+
+    def _encode_realm(self, realm):
+        # override default _encode_realm to fill in default realm field
+        if realm is None:
+            realm = self.default_realm
+            if realm is None:
+                raise TypeError("you must specify a realm explicitly, "
+                                  "or set the default_realm attribute")
+        return self._encode_field(realm, "realm")
+
+    #===================================================================
+    # public methods
+    #===================================================================
+
+    def realms(self):
+        """Return list of all realms in database"""
+        realms = set(key[1] for key in self._records)
+        return [self._decode_field(realm) for realm in realms]
+
+    def users(self, realm=None):
+        """Return list of all users in specified realm.
+
+        * uses ``self.default_realm`` if no realm explicitly provided.
+        * returns empty list if realm not found.
+        """
+        realm = self._encode_realm(realm)
+        return [self._decode_field(key[0]) for key in self._records
+                if key[1] == realm]
+
+    ##def has_user(self, user, realm=None):
+    ##    "check if user+realm combination exists"
+    ##    user = self._encode_user(user)
+    ##    realm = self._encode_realm(realm)
+    ##    return (user,realm) in self._records
+
+    ##def rename_realm(self, old, new):
+    ##    """rename all accounts in realm"""
+    ##    old = self._encode_realm(old)
+    ##    new = self._encode_realm(new)
+    ##    keys = [key for key in self._records if key[1] == old]
+    ##    for key in keys:
+    ##        hash = self._records.pop(key)
+    ##        self._records[key[0],new] = hash
+    ##    self._autosave()
+    ##    return len(keys)
+
+    ##def rename(self, old, new, realm=None):
+    ##    """rename user account"""
+    ##    old = self._encode_user(old)
+    ##    new = self._encode_user(new)
+    ##    realm = self._encode_realm(realm)
+    ##    hash = self._records.pop((old,realm))
+    ##    self._records[new,realm] = hash
+    ##    self._autosave()
+
+    def set_password(self, user, realm=None, password=_UNSET):
+        """Set password for user; adds user & realm if needed.
+
+        If ``self.default_realm`` has been set, this may be called
+        with the syntax ``set_password(user, password)``,
+        otherwise it must be called with all three arguments:
+        ``set_password(user, realm, password)``.
+
+        :returns:
+            * ``True`` if existing user was updated
+            * ``False`` if user account added.
+        """
+        if password is _UNSET:
+            # called w/ two args - (user, password), use default realm
+            realm, password = None, realm
+        user = self._encode_user(user)
+        realm = self._encode_realm(realm)
+        key = (user, realm)
+        existing = (key in self._records)
+        hash = htdigest.encrypt(password, user, realm, encoding=self.encoding)
+        if PY3:
+            hash = hash.encode(self.encoding)
+        self._records[key] = hash
+        self._autosave()
+        return existing
+
+    @deprecated_method(deprecated="1.6", removed="1.8",
+                       replacement="set_password")
+    def update(self, user, realm, password):
+        "set password for user"
+        return self.set_password(user, realm, password)
+
+    # XXX: rename to something more explicit, like get_hash()?
+    def get_hash(self, user, realm=None):
+        """Return :class:`~passlib.hash.htdigest` hash stored for user.
+
+        * uses ``self.default_realm`` if no realm explicitly provided.
+        * returns ``None`` if user or realm not found.
+
+        .. versionchanged:: 1.6
+            This method was previously named ``find``, it was renamed
+            for clarity. The old name is deprecated, and will be removed
+            in Passlib 1.8.
+        """
+        key = (self._encode_user(user), self._encode_realm(realm))
+        hash = self._records.get(key)
+        if hash is None:
+            return None
+        if PY3:
+            hash = hash.decode(self.encoding)
+        return hash
+
+    @deprecated_method(deprecated="1.6", removed="1.8",
+                       replacement="get_hash")
+    def find(self, user, realm):
+        "return hash for user"
+        return self.get_hash(user, realm)
+
+    # XXX: rename to something more explicit, like delete_user()?
+    def delete(self, user, realm=None):
+        """Delete user's entry for specified realm.
+
+        if realm is not specified, uses ``self.default_realm``.
+
+        :returns:
+            * ``True`` if user deleted,
+            * ``False`` if user not found in realm.
+        """
+        key = (self._encode_user(user), self._encode_realm(realm))
+        try:
+            del self._records[key]
+        except KeyError:
+            return False
+        self._autosave()
+        return True
+
+    def delete_realm(self, realm):
+        """Delete all users for specified realm.
+
+        if realm is not specified, uses ``self.default_realm``.
+
+        :returns: number of users deleted (0 if realm not found)
+        """
+        realm = self._encode_realm(realm)
+        records = self._records
+        keys = [key for key in records if key[1] == realm]
+        for key in keys:
+            del records[key]
+        self._autosave()
+        return len(keys)
+
+    def check_password(self, user, realm=None, password=_UNSET):
+        """Verify password for specified user + realm.
+
+        If ``self.default_realm`` has been set, this may be called
+        with the syntax ``check_password(user, password)``,
+        otherwise it must be called with all three arguments:
+        ``check_password(user, realm, password)``.
+
+        :returns:
+            * ``None`` if user or realm not found.
+            * ``False`` if user found, but password does not match.
+            * ``True`` if user found and password matches.
+
+        .. versionchanged:: 1.6
+            This method was previously called ``verify``, it was renamed
+            to prevent ambiguity with the :class:`!CryptContext` method.
+            The old alias is deprecated, and will be removed in Passlib 1.8.
+        """
+        if password is _UNSET:
+            # called w/ two args - (user, password), use default realm
+            realm, password = None, realm
+        user = self._encode_user(user)
+        realm = self._encode_realm(realm)
+        hash = self._records.get((user,realm))
+        if hash is None:
+            return None
+        return htdigest.verify(password, hash, user, realm,
+                               encoding=self.encoding)
+
+    @deprecated_method(deprecated="1.6", removed="1.8",
+                       replacement="check_password")
+    def verify(self, user, realm, password):
+        "verify password for user"
+        return self.check_password(user, realm, password)
+
+    #===================================================================
+    # eoc
+    #===================================================================
+
+#=============================================================================
+# eof
+#=============================================================================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/passlib/apps.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,184 @@
+"""passlib.apps"""
+#=============================================================================
+# imports
+#=============================================================================
+# core
+import logging; log = logging.getLogger(__name__)
+from itertools import chain
+# site
+# pkg
+from passlib import hash
+from passlib.context import LazyCryptContext
+from passlib.utils import sys_bits
+# local
+__all__ = [
+    'custom_app_context',
+    'django_context',
+    'ldap_context', 'ldap_nocrypt_context',
+    'mysql_context', 'mysql4_context', 'mysql3_context',
+    'phpass_context',
+    'phpbb3_context',
+    'postgres_context',
+]
+
+#=============================================================================
+# master containing all identifiable hashes
+#=============================================================================
+def _load_master_config():
+    from passlib.registry import list_crypt_handlers
+
+    # get master list
+    schemes = list_crypt_handlers()
+
+    # exclude the ones we know have ambiguous or greedy identify() methods.
+    excluded = [
+        # frequently confused for eachother
+        'bigcrypt',
+        'crypt16',
+
+        # no good identifiers
+        'cisco_pix',
+        'cisco_type7',
+        'htdigest',
+        'mysql323',
+        'oracle10',
+
+        # all have same size
+        'lmhash',
+        'msdcc',
+        'msdcc2',
+        'nthash',
+
+        # plaintext handlers
+        'plaintext',
+        'ldap_plaintext',
+
+        # disabled handlers
+        'django_disabled',
+        'unix_disabled',
+        'unix_fallback',
+    ]
+    for name in excluded:
+        schemes.remove(name)
+
+    # return config
+    return dict(schemes=schemes, default="sha256_crypt")
+master_context = LazyCryptContext(onload=_load_master_config)
+
+#=============================================================================
+# for quickly bootstrapping new custom applications
+#=============================================================================
+custom_app_context = LazyCryptContext(
+    # choose some reasonbly strong schemes
+    schemes=["sha512_crypt", "sha256_crypt"],
+
+    # set some useful global options
+    default="sha256_crypt" if sys_bits < 64 else "sha512_crypt",
+    all__vary_rounds = 0.1,
+
+    # set a good starting point for rounds selection
+    sha512_crypt__min_rounds = 60000,
+    sha256_crypt__min_rounds = 80000,
+
+    # if the admin user category is selected, make a much stronger hash,
+    admin__sha512_crypt__min_rounds = 120000,
+    admin__sha256_crypt__min_rounds = 160000,
+    )
+
+#=============================================================================
+# django
+#=============================================================================
+_django10_schemes = [
+        "django_salted_sha1", "django_salted_md5", "django_des_crypt",
+        "hex_md5", "django_disabled",
+]
+
+django10_context = LazyCryptContext(
+    schemes=_django10_schemes,
+    default="django_salted_sha1",
+    deprecated=["hex_md5"],
+)
+
+django14_context = LazyCryptContext(
+    schemes=["django_pbkdf2_sha256", "django_pbkdf2_sha1", "django_bcrypt"] \
+            + _django10_schemes,
+    deprecated=_django10_schemes,
+)
+
+# this will always point to latest version
+django_context = django14_context
+
+#=============================================================================
+# ldap
+#=============================================================================
+std_ldap_schemes = ["ldap_salted_sha1", "ldap_salted_md5",
+                      "ldap_sha1", "ldap_md5",
+                      "ldap_plaintext" ]
+
+# create context with all std ldap schemes EXCEPT crypt
+ldap_nocrypt_context = LazyCryptContext(std_ldap_schemes)
+
+# create context with all possible std ldap + ldap crypt schemes
+def _iter_ldap_crypt_schemes():
+    from passlib.utils import unix_crypt_schemes
+    return ('ldap_' + name for name in unix_crypt_schemes)
+
+def _iter_ldap_schemes():
+    "helper which iterates over supported std ldap schemes"
+    return chain(std_ldap_schemes, _iter_ldap_crypt_schemes())
+ldap_context = LazyCryptContext(_iter_ldap_schemes())
+
+### create context with all std ldap schemes + crypt schemes for localhost
+##def _iter_host_ldap_schemes():
+##    "helper which iterates over supported std ldap schemes"
+##    from passlib.handlers.ldap_digests import get_host_ldap_crypt_schemes
+##    return chain(std_ldap_schemes, get_host_ldap_crypt_schemes())
+##ldap_host_context = LazyCryptContext(_iter_host_ldap_schemes())
+
+#=============================================================================
+# mysql
+#=============================================================================
+mysql3_context = LazyCryptContext(["mysql323"])
+mysql4_context = LazyCryptContext(["mysql41", "mysql323"], deprecated="mysql323")
+mysql_context = mysql4_context # tracks latest mysql version supported
+
+#=============================================================================
+# postgres
+#=============================================================================
+postgres_context = LazyCryptContext(["postgres_md5"])
+
+#=============================================================================
+# phpass & variants
+#=============================================================================
+def _create_phpass_policy(**kwds):
+    "helper to choose default alg based on bcrypt availability"
+    kwds['default'] = 'bcrypt' if hash.bcrypt.has_backend() else 'phpass'
+    return kwds
+
+phpass_context = LazyCryptContext(
+    schemes=["bcrypt", "phpass", "bsdi_crypt"],
+    onload=_create_phpass_policy,
+    )
+
+phpbb3_context = LazyCryptContext(["phpass"], phpass__ident="H")
+
+# TODO: support the drupal phpass variants (see phpass homepage)
+
+#=============================================================================
+# roundup
+#=============================================================================
+
+_std_roundup_schemes = [ "ldap_hex_sha1", "ldap_hex_md5", "ldap_des_crypt", "roundup_plaintext" ]
+roundup10_context = LazyCryptContext(_std_roundup_schemes)
+
+# NOTE: 'roundup15' really applies to roundup 1.4.17+
+roundup_context = roundup15_context = LazyCryptContext(
+    schemes=_std_roundup_schemes + [ "ldap_pbkdf2_sha1" ],
+    deprecated=_std_roundup_schemes,
+    default = "ldap_pbkdf2_sha1",
+    ldap_pbkdf2_sha1__default_rounds = 10000,
+    )
+
+#=============================================================================
+# eof
+#=============================================================================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/passlib/context.py	Sun Mar 24 14:58:56 2013 +0100
@@ -0,0 +1,2687 @@
+"""passlib.context - CryptContext implementation"""
+#=============================================================================
+# imports
+#=============================================================================
+from __future__ import with_statement
+# core
+from functools import update_wrapper
+import inspect
+import re
+import hashlib
+from math import log as logb, ceil
+import logging; log = logging.getLogger(__name__)
+import os
+import re
+from time import sleep
+from warnings import warn
+# site
+# pkg
+from passlib.exc import PasslibConfigWarning, ExpectedStringError, ExpectedTypeError
+from passlib.registry import get_crypt_handler, _validate_handler_name
+from passlib.utils import rng, tick, to_bytes, deprecated_method, \
+                          to_unicode, splitcomma
+from passlib.utils.compat import bytes, iteritems, num_types, \
+                                 PY2, PY3, PY_MIN_32, unicode, SafeConfigParser, \
+                                 NativeStringIO, BytesIO, base_string_types
+# local
+__all__ = [
+    'CryptContext',
+    'LazyCryptContext',
+    'CryptPolicy',
+]
+
+#=============================================================================
+# support
+#=============================================================================
+
+# private object to detect unset params
+_UNSET = object()
+
+# TODO: merge the following helpers into _CryptConfig
+
+def _coerce_vary_rounds(value):
+    "parse vary_rounds string to percent as [0,1) float, or integer"
+    if value.endswith("%"):
+        # XXX: deprecate this in favor of raw float?
+        return float(value.rstrip("%"))*.01
+    try:
+        return int(value)
+    except ValueError:
+        return float(value)
+
+# set of options which aren't allowed to be set via policy
+_forbidden_scheme_options = set(["salt"])
+    # 'salt' - not allowed since a fixed salt would defeat the purpose.
+
+# dict containing funcs used to coerce strings to correct type
+# for scheme option keys.
+_coerce_scheme_options = dict(
+    min_rounds=int,
+    max_rounds=int,
+    default_rounds=int,
+    vary_rounds=_coerce_vary_rounds,
+    salt_size=int,
+)
+
+def _is_handler_registered(handler):
+    """detect if handler is registered or a custom handler"""
+    return get_crypt_handler(handler.name, None) is handler
+
+#=============================================================================
+# crypt policy
+#=============================================================================
+_preamble = ("The CryptPolicy class has been deprecated as of "
+             "Passlib 1.6, and will be removed in Passlib 1.8. ")
+
+class CryptPolicy(object):
+    """
+    .. deprecated:: 1.6
+        This class has been deprecated, and will be removed in Passlib 1.8.
+        All of it's functionality has been rolled into :class:`CryptContext`.
+
+    This class previously stored the configuration options for the
+    CryptContext class. In the interest of interface simplification,
+    all of this class' functionality has been rolled into the CryptContext
+    class itself.
+    The documentation for this class is now focused on  documenting how to
+    migrate to the new api. Additionally, where possible, the deprecation
+    warnings issued by the CryptPolicy methods will list the replacement call
+    that should be used.
+
+    Constructors
+    ============
+    CryptPolicy objects can be constructed directly using any of
+    the keywords accepted by :class:`CryptContext`. Direct uses of the
+    :class:`!CryptPolicy` constructor should either pass the keywords
+    directly into the CryptContext constructor, or to :meth:`CryptContext.update`
+    if the policy object was being used to update an existing context object.
+
+    In addition to passing in keywords directly,
+    CryptPolicy objects can be constructed by the following methods:
+
+    .. automethod:: from_path
+    .. automethod:: from_string
+    .. automethod:: from_source
+    .. automethod:: from_sources
+    .. automethod:: replace
+
+    Introspection
+    =============
+    All of the informational methods provided by this class have been deprecated
+    by identical or similar methods in the :class:`CryptContext` class:
+
+    .. automethod:: has_schemes
+    .. automethod:: schemes
+    .. automethod:: iter_handlers
+    .. automethod:: get_handler
+    .. automethod:: get_options
+    .. automethod:: handler_is_deprecated
+    .. automethod:: get_min_verify_time
+
+    Exporting
+    =========
+    .. automethod:: iter_config
+    .. automethod:: to_dict
+    .. automethod:: to_file
+    .. automethod:: to_string
+
+    .. note::
+        CryptPolicy are immutable.
+        Use the :meth:`replace` method to mutate existing instances.
+
+    .. deprecated:: 1.6
+    """
+    #===================================================================
+    # class methods
+    #===================================================================
+    @classmethod
+    def from_path(cls, path, section="passlib", encoding="utf-8"):
+        """create a CryptPolicy instance from a local file.
+
+        .. deprecated:: 1.6
+
+        Creating a new CryptContext from a file, which was previously done via
+        ``CryptContext(policy=CryptPolicy.from_path(path))``, can now be
+        done via ``CryptContext.from_path(path)``.
+        See :meth:`CryptContext.from_path` for details.
+
+        Updating an existing CryptContext from a file, which was previously done
+        ``context.policy = CryptPolicy.from_path(path)``, can now be
+        done via ``context.load_path(path)``.
+        See :meth:`CryptContext.load_path` for details.
+        """
+        warn(_preamble +
+             "Instead of ``CryptPolicy.from_path(path)``, "
+             "use ``CryptContext.from_path(path)`` "
+             " or ``context.load_path(path)`` for an existing CryptContext.",
+             DeprecationWarning, stacklevel=2)
+        return cls(_internal_context=CryptContext.from_path(path, section,
+                                                            encoding))
+
+    @classmethod
+    def from_string(cls, source, section="passlib", encoding="utf-8"):
+        """create a CryptPolicy instance from a string.
+
+        .. deprecated:: 1.6
+
+        Creating a new CryptContext from a string, which was previously done via
+        ``CryptContext(policy=CryptPolicy.from_string(data))``, can now be
+        done via ``CryptContext.from_string(data)``.
+        See :meth:`CryptContext.from_string` for details.
+
+        Updating an existing CryptContext from a string, which was previously done
+        ``context.policy = CryptPolicy.from_string(data)``, can now be
+        done via ``context.load(data)``.
+        See :meth:`CryptContext.load` for details.
+        """
+        warn(_preamble +
+             "Instead of ``CryptPolicy.from_string(source)``, "
+             "use ``CryptContext.from_string(source)`` or "
+             "``context.load(source)`` for an existing CryptContext.",
+             DeprecationWarning, stacklevel=2)
+        return cls(_internal_context=CryptContext.from_string(source, section,
+                                                              encoding))
+
+    @classmethod
+    def from_source(cls, source, _warn=True):
+        """create a CryptPolicy instance from some source.
+
+        this method autodetects the source type, and invokes
+        the appropriate constructor automatically. it attempts
+        to detect whether the source is a configuration string, a filepath,
+        a dictionary, or an existing CryptPolicy instance.
+
+        .. deprecated:: 1.6
+
+        Create a new CryptContext, which could previously be done via
+        ``CryptContext(policy=CryptPolicy.from_source(source))``, should
+        now be done using an explicit method: the :class:`CryptContext`
+        constructor itself, :meth:`CryptContext.from_path`,
+        or :meth:`CryptContext.from_string`.
+
+        Updating an existing CryptContext, which could previously be done via
+        ``context.policy = CryptPolicy.from_source(source)``, should
+        now be done using an explicit method: :meth:`CryptContext.update`,
+        or :meth:`CryptContext.load`.
+        """
+        if _warn:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy.from_source()``, "
+                 "use ``CryptContext.from_string(path)`` "
+                 " or ``CryptContext.from_path(source)``, as appropriate.",
+                 DeprecationWarning, stacklevel=2)
+        if isinstance(source, CryptPolicy):
+            return source
+        elif isinstance(source, dict):
+            return cls(_internal_context=CryptContext(**source))
+        elif not isinstance(source, (bytes,unicode)):
+            raise TypeError("source must be CryptPolicy, dict, config string, "
+                            "or file path: %r" % (type(source),))
+        elif any(c in source for c in "\n\r\t") or not source.strip(" \t./\;:"):
+            return cls(_internal_context=CryptContext.from_string(source))
+        else:
+            return cls(_internal_context=CryptContext.from_path(source))
+
+    @classmethod
+    def from_sources(cls, sources, _warn=True):
+        """create a CryptPolicy instance by merging multiple sources.
+
+        each source is interpreted as by :meth:`from_source`,
+        and the results are merged together.
+
+        .. deprecated:: 1.6
+            Instead of using this method to merge multiple policies together,
+            a :class:`CryptContext` instance should be created, and then
+            the multiple sources merged together via :meth:`CryptContext.load`.
+        """
+        if _warn:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy.from_sources()``, "
+                 "use the various CryptContext constructors "
+                 " followed by ``context.update()``.",
+                 DeprecationWarning, stacklevel=2)
+        if len(sources) == 0:
+            raise ValueError("no sources specified")
+        if len(sources) == 1:
+            return cls.from_source(sources[0], _warn=False)
+        kwds = {}
+        for source in sources:
+            kwds.update(cls.from_source(source, _warn=False)._context.to_dict(resolve=True))
+        return cls(_internal_context=CryptContext(**kwds))
+
+    def replace(self, *args, **kwds):
+        """create a new CryptPolicy, optionally updating parts of the
+        existing configuration.
+
+        .. deprecated:: 1.6
+            Callers of this method should :meth:`CryptContext.update` or
+            :meth:`CryptContext.copy` instead.
+        """
+        if self._stub_policy:
+            warn(_preamble + # pragma: no cover -- deprecated & unused
+                 "Instead of ``context.policy.replace()``, "
+                 "use ``context.update()`` or ``context.copy()``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().replace()``, "
+                 "create a CryptContext instance and "
+                 "use ``context.update()`` or ``context.copy()``.",
+                 DeprecationWarning, stacklevel=2)
+        sources = [ self ]
+        if args:
+            sources.extend(args)
+        if kwds:
+            sources.append(kwds)
+        return CryptPolicy.from_sources(sources, _warn=False)
+
+    #===================================================================
+    # instance attrs
+    #===================================================================
+
+    # internal CryptContext we're wrapping to handle everything
+    # until this class is removed.
+    _context = None
+
+    # flag indicating this is wrapper generated by the CryptContext.policy
+    # attribute, rather than one created independantly by the application.
+    _stub_policy = False
+
+    #===================================================================
+    # init
+    #===================================================================
+    def __init__(self, *args, **kwds):
+        context = kwds.pop("_internal_context", None)
+        if context:
+            assert isinstance(context, CryptContext)
+            self._context = context
+            self._stub_policy = kwds.pop("_stub_policy", False)
+            assert not (args or kwds), "unexpected args: %r %r" % (args,kwds)
+        else:
+            if args:
+                if len(args) != 1:
+                    raise TypeError("only one positional argument accepted")
+                if kwds:
+                    raise TypeError("cannot specify positional arg and kwds")
+                kwds = args[0]
+            warn(_preamble +
+                 "Instead of constructing a CryptPolicy instance, "
+                 "create a CryptContext directly, or use ``context.update()`` "
+                 "and ``context.load()`` to reconfigure existing CryptContext "
+                 "instances.",
+                 DeprecationWarning, stacklevel=2)
+            self._context = CryptContext(**kwds)
+
+    #===================================================================
+    # public interface for examining options
+    #===================================================================
+    def has_schemes(self):
+        """return True if policy defines *any* schemes for use.
+
+        .. deprecated:: 1.6
+            applications should use ``bool(context.schemes())`` instead.
+            see :meth:`CryptContext.schemes`.
+        """
+        if self._stub_policy:
+            warn(_preamble + # pragma: no cover -- deprecated & unused
+                 "Instead of ``context.policy.has_schemes()``, "
+                 "use ``bool(context.schemes())``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().has_schemes()``, "
+                 "create a CryptContext instance and "
+                 "use ``bool(context.schemes())``.",
+                 DeprecationWarning, stacklevel=2)
+        return bool(self._context.schemes())
+
+    def iter_handlers(self):
+        """return iterator over handlers defined in policy.
+
+        .. deprecated:: 1.6
+            applications should use ``context.schemes(resolve=True))`` instead.
+            see :meth:`CryptContext.schemes`.
+        """
+        if self._stub_policy:
+            warn(_preamble +
+                 "Instead of ``context.policy.iter_handlers()``, "
+                 "use ``context.schemes(resolve=True)``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().iter_handlers()``, "
+                 "create a CryptContext instance and "
+                 "use ``context.schemes(resolve=True)``.",
+                 DeprecationWarning, stacklevel=2)
+        return self._context.schemes(resolve=True)
+
+    def schemes(self, resolve=False):
+        """return list of schemes defined in policy.
+
+        .. deprecated:: 1.6
+            applications should use :meth:`CryptContext.schemes` instead.
+        """
+        if self._stub_policy:
+            warn(_preamble + # pragma: no cover -- deprecated & unused
+                 "Instead of ``context.policy.schemes()``, "
+                 "use ``context.schemes()``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().schemes()``, "
+                 "create a CryptContext instance and "
+                 "use ``context.schemes()``.",
+                 DeprecationWarning, stacklevel=2)
+        return list(self._context.schemes(resolve=resolve))
+
+    def get_handler(self, name=None, category=None, required=False):
+        """return handler as specified by name, or default handler.
+
+        .. deprecated:: 1.6
+            applications should use :meth:`CryptContext.handler` instead,
+            though note that the ``required`` keyword has been removed,
+            and the new method will always act as if ``required=True``.
+        """
+        if self._stub_policy:
+            warn(_preamble +
+                 "Instead of ``context.policy.get_handler()``, "
+                 "use ``context.handler()``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().get_handler()``, "
+                 "create a CryptContext instance and "
+                 "use ``context.handler()``.",
+                 DeprecationWarning, stacklevel=2)
+        # CryptContext.handler() doesn't support required=False,
+        # so wrapping it in try/except
+        try:
+            return self._context.handler(name, category)
+        except KeyError:
+            if required:
+                raise
+            else:
+                return None
+
+    def get_min_verify_time(self, category=None):
+        """get min_verify_time setting for policy.
+
+        .. deprecated:: 1.6
+            min_verify_time will be removed entirely in passlib 1.8
+        """
+        warn("get_min_verify_time() and min_verify_time option is deprecated, "
+             "and will be removed in Passlib 1.8", DeprecationWarning,
+             stacklevel=2)
+        return self._context._config.get_context_option_with_flag(category, "min_verify_time")[0] or 0
+
+    def get_options(self, name, category=None):
+        """return dictionary of options specific to a given handler.
+
+        .. deprecated:: 1.6
+            this method has no direct replacement in the 1.6 api, as there
+            is not a clearly defined use-case. however, examining the output of
+            :meth:`CryptContext.to_dict` should serve as the closest alternative.
+        """
+        # XXX: might make a public replacement, but need more study of the use cases.
+        if self._stub_policy:
+            warn(_preamble + # pragma: no cover -- deprecated & unused
+                 "``context.policy.get_options()`` will no longer be available.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "``CryptPolicy().get_options()`` will no longer be available.",
+                 DeprecationWarning, stacklevel=2)
+        if hasattr(name, "name"):
+            name = name.name
+        return self._context._config._get_record_options_with_flag(name, category)[0]
+
+    def handler_is_deprecated(self, name, category=None):
+        """check if handler has been deprecated by policy.
+
+        .. deprecated:: 1.6
+            this method has no direct replacement in the 1.6 api, as there
+            is not a clearly defined use-case. however, examining the output of
+            :meth:`CryptContext.to_dict` should serve as the closest alternative.
+        """
+        # XXX: might make a public replacement, but need more study of the use cases.
+        if self._stub_policy:
+            warn(_preamble +
+                 "``context.policy.handler_is_deprecated()`` will no longer be available.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "``CryptPolicy().handler_is_deprecated()`` will no longer be available.",
+                 DeprecationWarning, stacklevel=2)
+        if hasattr(name, "name"):
+            name = name.name
+        return self._context._is_deprecated_scheme(name, category)
+
+    #===================================================================
+    # serialization
+    #===================================================================
+
+    def iter_config(self, ini=False, resolve=False):
+        """iterate over key/value pairs representing the policy object.
+
+        .. deprecated:: 1.6
+            applications should use :meth:`CryptContext.to_dict` instead.
+        """
+        if self._stub_policy:
+            warn(_preamble + # pragma: no cover -- deprecated & unused
+                 "Instead of ``context.policy.iter_config()``, "
+                 "use ``context.to_dict().items()``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().iter_config()``, "
+                 "create a CryptContext instance and "
+                 "use ``context.to_dict().items()``.",
+                 DeprecationWarning, stacklevel=2)
+        # hacked code that renders keys & values in manner that approximates
+        # old behavior. context.to_dict() is much cleaner.
+        context = self._context
+        if ini:
+            def render_key(key):
+                return context._render_config_key(key).replace("__", ".")
+            def render_value(value):
+                if isinstance(value, (list,tuple)):
+                    value = ", ".join(value)
+                return value
+            resolve = False
+        else:
+            render_key = context._render_config_key
+            render_value = lambda value: value
+        return (
+            (render_key(key), render_value(value))
+            for key, value in context._config.iter_config(resolve)
+        )
+
+    def to_dict(self, resolve=False):
+        """export policy object as dictionary of options.
+
+        .. deprecated:: 1.6
+            applications should use :meth:`CryptContext.to_dict` instead.
+        """
+        if self._stub_policy:
+            warn(_preamble +
+                 "Instead of ``context.policy.to_dict()``, "
+                 "use ``context.to_dict()``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().to_dict()``, "
+                 "create a CryptContext instance and "
+                 "use ``context.to_dict()``.",
+                 DeprecationWarning, stacklevel=2)
+        return self._context.to_dict(resolve)
+
+    def to_file(self, stream, section="passlib"): # pragma: no cover -- deprecated & unused
+        """export policy to file.
+
+        .. deprecated:: 1.6
+            applications should use :meth:`CryptContext.to_string` instead,
+            and then write the output to a file as desired.
+        """
+        if self._stub_policy:
+            warn(_preamble +
+                 "Instead of ``context.policy.to_file(stream)``, "
+                 "use ``stream.write(context.to_string())``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().to_file(stream)``, "
+                 "create a CryptContext instance and "
+                 "use ``stream.write(context.to_string())``.",
+                 DeprecationWarning, stacklevel=2)
+        out = self._context.to_string(section=section)
+        if PY2:
+            out = out.encode("utf-8")
+        stream.write(out)
+
+    def to_string(self, section="passlib", encoding=None):
+        """export policy to file.
+
+        .. deprecated:: 1.6
+            applications should use :meth:`CryptContext.to_string` instead.
+        """
+        if self._stub_policy:
+            warn(_preamble + # pragma: no cover -- deprecated & unused
+                 "Instead of ``context.policy.to_string()``, "
+                 "use ``context.to_string()``.",
+                 DeprecationWarning, stacklevel=2)
+        else:
+            warn(_preamble +
+                 "Instead of ``CryptPolicy().to_string()``, "
+                 "create a CryptContext instance and "
+                 "use ``context.to_string()``.",
+                 DeprecationWarning, stacklevel=2)
+        out = self._context.to_string(section=section)
+        if encoding:
+            out = out.encode(encoding)
+        return out
+
+    #===================================================================
+    # eoc
+    #===================================================================
+
+#=============================================================================
+# _CryptRecord helper class
+#=============================================================================
+class _CryptRecord(object):
+    """wraps a handler and automatically applies various options.
+
+    this is a helper used internally by CryptContext in order to reduce the
+    amount of work that needs to be done by CryptContext.verify().
+    this class takes in all the options for a particular (scheme, category)
+    combination, and attempts to provide as short a code-path as possible for
+    the particular configuration.
+    """
+
+    #===================================================================
+    # instance attrs
+    #===================================================================
+
+    # informational attrs
+    handler = None # handler instance this is wrapping
+    category = None # user category this applies to
+    deprecated = False # set if handler itself has been deprecated in config
+
+    # rounds management - filled in by _init_rounds_options()
+    _has_rounds_options = False # if _has_rounds_bounds OR _generate_rounds is set
+    _has_rounds_bounds = False # if either min_rounds or max_rounds set
+    _min_rounds = None # minimum rounds allowed by policy, or None
+    _max_rounds = None # maximum rounds allowed by policy, or None
+    _generate_rounds = None # rounds generation function, or None
+
+    # encrypt()/genconfig() attrs
+    settings = None # options to be passed directly to encrypt()
+
+    # verify() attrs
+    _min_verify_time = None
+
+    # needs_update() attrs
+    _needs_update = None # optional callable provided by handler
+    _has_rounds_introspection = False # if rounds can be extract from hash
+
+    # cloned directly from handler, not affected by config options.
+    identify = None
+    genhash = None
+
+    #===================================================================
+    # init
+    #===================================================================
+    def __init__(self, handler, category=None, deprecated=False,
+                 min_rounds=None, max_rounds=None, default_rounds=None,
+                 vary_rounds=None, min_verify_time=None,
+                 **settings):
+        # store basic bits
+        self.handler = handler
+        self.category = category
+        self.deprecated = deprecated
+        self.settings = settings
+
+        # validate & normalize rounds options
+        self._init_rounds_options(min_rounds, max_rounds, default_rounds,
+                             vary_rounds)
+
+        # init wrappers for handler methods we modify args to
+        self._init_encrypt_and_genconfig()
+        self._init_verify(min_verify_time)
+        self._init_needs_update()
+
+        # these aren't wrapped by _CryptRecord, copy them directly from handler.
+        self.identify = handler.identify
+        self.genhash = handler.genhash
+
+    #===================================================================
+    # virtual attrs
+    #===================================================================
+    @property
+    def scheme(self):
+        return self.handler.name
+
+    @property
+    def _errprefix(self):
+        "string used to identify record in error messages"
+        handler = self.handler
+        category = self.category
+        if category:
+            return "%s %s config" % (handler.name, category)
+        else:
+            return "%s config" % (handler.name,)
+
+    def __repr__(self): # pragma: no cover -- debugging
+        return "<_CryptRecord 0x%x for %s>" % (id(self), self._errprefix)
+
+    #===================================================================
+    # rounds generation & limits - used by encrypt & deprecation code
+    #===================================================================
+    def _init_rounds_options(self, mn, mx, df, vr):
+        "parse options and compile efficient generate_rounds function"
+        #----------------------------------------------------
+        # extract hard limits from handler itself
+        #----------------------------------------------------
+        handler = self.handler
+        if 'rounds' not in handler.setting_kwds:
+            # doesn't even support rounds keyword.
+            return
+        hmn = getattr(handler, "min_rounds", None)
+        hmx = getattr(handler, "max_rounds", None)
+
+        def check_against_handler(value, name):
+            "issue warning if value outside handler limits"
+            if hmn is not None and value < hmn:
+                warn("%s: %s value is below handler minimum %d: %d" %
+                     (self._errprefix, name, hmn, value), PasslibConfigWarning)
+            if hmx is not None and value > hmx:
+                warn("%s: %s value is above handler maximum %d: %d" %
+                     (self._errprefix, name, hmx, value), PasslibConfigWarning)
+
+        #----------------------------------------------------
+        # set policy limits
+        #----------------------------------------------------
+        if mn is not None:
+            if mn < 0:
+                raise ValueError("%s: min_rounds must be >= 0" % self._errprefix)
+            check_against_handler(mn, "min_rounds")
+            self._min_rounds = mn
+            self._has_rounds_bounds = True
+
+        if mx is not None:
+            if mn is not None and mx < mn:
+                raise ValueError("%s: max_rounds must be "
+                                 ">= min_rounds" % self._errprefix)
+            elif mx < 0:
+                raise ValueError("%s: max_rounds must be >= 0" % self._errprefix)
+            check_against_handler(mx, "max_rounds")
+            self._max_rounds = mx
+            self._has_rounds_bounds = True
+
+        #----------------------------------------------------
+        # validate default_rounds
+        #----------------------------------------------------
+        if df is not None:
+            if mn is not None and df < mn:
+                    raise ValueError("%s: default_rounds must be "
+                                     ">= min_rounds" % self._errprefix)
+            if mx is not None and df > mx:
+                    raise ValueError("%s: default_rounds must be "
+                                     "<= max_rounds" % self._errprefix)
+            check_against_handler(df, "default_rounds")
+        elif vr or mx or mn:
+            # need an explicit default to work with
+            df = getattr(handler, "default_rounds", None) or mx or mn
+            assert df is not None, "couldn't find fallback default_rounds"
+        else:
+            # no need for rounds generation
+            self._has_rounds_options = self._has_rounds_bounds
+            return
+
+        # clip default to handler & policy limits *before* vary rounds
+        # is calculated, so that proportion vr values are scaled against
+        # the effective default.
+        def clip(value):
+            "clip value to intersection of policy + handler limits"
+            if mn is not None and value < mn:
+                value = mn
+            if hmn is not None and value < hmn:
+                value = hmn
+            if mx is not None and value > mx:
+                value = mx
+            if hmx is not None and value > hmx:
+                value = hmx
+            return value
+        df = clip(df)
+
+        #----------------------------------------------------
+        # validate vary_rounds,
+        # coerce df/vr to linear scale,
+        # and setup scale_value() to undo coercion
+        #----------------------------------------------------
+        # NOTE: vr=0 same as if vr not set
+        if vr:
+            if vr < 0:
+                raise ValueError("%s: vary_rounds must be >= 0" %
+                                 self._errprefix)
+            def scale_value(value, upper):
+                return value
+            if isinstance(vr, float):
+                # vr is value from 0..1 expressing fraction of default rounds.
+                if vr > 1:
+                    # XXX: deprecate 1.0 ?
+                    raise ValueError("%s: vary_rounds must be < 1.0" %
+                                     self._errprefix)
+                # calculate absolute vr value based on df & rounds_cost
+                cost_scale = getattr(handler, "rounds_cost", "linear")
+                assert cost_scale in ["log2", "linear"]
+                if cost_scale == "log2":
+                    # convert df & vr to linear scale for limit calc,
+                    # and redefine scale_value() to convert back to log2.
+                    df = 1<<df
+                    def scale_value(value, upper):
+                        if value <= 0:
+                            return 0
+                        elif upper:
+                            return int(logb(value,2))
+                        else:
+                            return int(ceil(logb(value,2)))
+                vr = int(df*vr)
+            elif not isinstance(vr, int):
+                raise TypeError("vary_rounds must be int or float")
+            # else: vr is explicit number of rounds to vary df by.
+
+        #----------------------------------------------------
+        # set up rounds generation function.
+        #----------------------------------------------------
+        if not vr:
+            # fixed rounds value
+            self._generate_rounds = lambda : df
+        else:
+            # randomly generate rounds in range df +/- vr
+            lower = clip(scale_value(df-vr,False))
+            upper = clip(scale_value(df+vr,True))
+            if lower == upper:
+                self._generate_rounds = lambda: upper
+            else:
+                assert lower < upper
+                self._generate_rounds = lambda: rng.randint(lower, upper)
+
+        # hack for bsdi_crypt - want to avoid even-valued rounds
+        # NOTE: this technically might generate a rounds value 1 larger
+        # than the requested upper bound - but better to err on side of safety.
+        if getattr(handler, "_avoid_even_rounds", False):
+            gen = self._generate_rounds
+            self._generate_rounds = lambda : gen()|1
+
+        self._has_rounds_options = True
+
+    #===================================================================
+    # encrypt() / genconfig()
+    #===================================================================
+    def _init_encrypt_and_genconfig(self):
+        "initialize genconfig/encrypt wrapper methods"
+        settings = self.settings
+        handler = self.handler
+
+        # check no invalid settings are being set
+        keys = handler.setting_kwds
+        for key in settings:
+            if key not in keys:
+                raise KeyError("keyword not supported by %s handler: %r" %
+                               (handler.name, key))
+
+        # if _prepare_settings() has nothing to do, bypass our wrappers
+        # with reference to original methods.
+        if not (settings or self._has_rounds_options):
+            self.genconfig = handler.genconfig
+            self.encrypt = handler.encrypt
+
+    def genconfig(self, **kwds):
+        "wrapper for handler.genconfig() which adds custom settings/rounds"
+        self._prepare_settings(kwds)
+        return self.handler.genconfig(**kwds)
+
+    def encrypt(self, secret, **kwds):
+        "wrapper for handler.encrypt() which adds custom settings/rounds"
+        self._prepare_settings(kwds)
+        return self.handler.encrypt(secret, **kwds)
+
+    def _prepare_settings(self, kwds):
+        "add default values to settings for encrypt & genconfig"
+        # load in default values for any settings
+        if kwds:
+            for k,v in iteritems(self.settings):
+                if k not in kwds:
+                    kwds[k] = v
+        else:
+            # faster, and the common case
+            kwds.update(self.settings)
+
+        # handle rounds
+        if self._has_rounds_options:
+            rounds = kwds.get("rounds")
+            if rounds is None:
+                # fill in default rounds value
+                gen = self._generate_rounds
+                if gen:
+                    kwds['rounds'] = gen()
+            elif self._has_rounds_bounds:
+                # check bounds for application-provided rounds value.
+                # XXX: should this raise an error instead of warning ?
+                # NOTE: stackdepth=4 is so that error matches
+                # where ctx.encrypt() was called by application code.
+                mn = self._min_rounds
+                if mn is not None and rounds < mn:
+                    warn("%s requires rounds >= %d, increasing value from %d" %
+                         (self._errprefix, mn, rounds), PasslibConfigWarning, 4)
+                    rounds = mn
+                mx = self._max_rounds
+                if mx and rounds > mx:
+                    warn("%s requires rounds <= %d, decreasing value from %d" %
+                         (self._errprefix, mx, rounds), PasslibConfigWarning, 4)
+                    rounds = mx
+                kwds['rounds'] = rounds
+
+    #===================================================================
+    # verify()
+    #===================================================================
+    # TODO: once min_verify_time is removed, this will just be a clone
+    # of handler.verify()
+
+    def _init_verify(self, mvt):
+        "initialize verify() wrapper - implements min_verify_time"
+        if mvt:
+            assert isinstance(mvt, (int,float)) and mvt > 0, "CryptPolicy should catch this"
+            self._min_verify_time = mvt
+        else:
+            # no mvt wrapper needed, so just use handler.verify directly
+            self.verify = self.handler.verify
+
+    def verify(self, secret, hash, **context):
+        "verify helper - adds min_verify_time delay"
+        mvt = self._min_verify_time
+        assert mvt > 0, "wrapper should have been replaced for mvt=0"
+        start = tick()
+        if self.handler.verify(secret, hash, **context):
+            return True
+        end = tick()
+        delta = mvt + start - end
+        if delta > 0:
+            sleep(delta)
+        elif delta < 0:
+            # warn app they exceeded bounds (this might reveal
+            # relative costs of different hashes if under migration)
+            warn("CryptContext: verify exceeded min_verify_time: "
+                 "scheme=%r min_verify_time=%r elapsed=%r" %
+                 (self.scheme, mvt, end-start), PasslibConfigWarning)
+        return False
+
+    #===================================================================
+    # needs_update()
+    #===================================================================
+    def _init_needs_update(self):
+        """initialize state for needs_update()"""
+        # if handler has been deprecated, replace wrapper and skip other checks
+        if self.deprecated:
+            self.needs_update = lambda hash, secret: True
+            return
+
+        # let handler detect hashes with configurations that don't match
+        # current settings. currently do this by calling
+        # ``handler._bind_needs_update(**settings)``, which if defined
+        # should return None or a callable ``needs_update(hash,secret)->bool``.
+        #
+        # NOTE: this interface is still private, because it was hacked in
+        # for the sake of bcrypt & scram, and is subject to change.
+        handler = self.handler
+        const = getattr(handler, "_bind_needs_update", None)
+        if const:
+            self._needs_update = const(**self.settings)
+
+        # XXX: what about a "min_salt_size" deprecator?
+
+        # set flag if we can extract rounds from hash, allowing
+        # needs_update() to check for rounds that are outside of
+        # the configured range.
+        if self._has_rounds_bounds and hasattr(handler, "from_string"):
+            self._has_rounds_introspection = True
+
+    def needs_update(self, hash, secret):
+        # init replaces this method entirely for this case.
+        ### check if handler has been deprecated
+        ##if self.deprecated:
+        ##    return True
+
+        # check handler's detector if it provided one.
+        check = self._needs_update
+        if check and check(hash, secret):
+            return True
+
+        # XXX: should we use from_string() call below to check
+        #      for config strings, and flag them as needing update?
+        #      or throw an error?
+        #      or leave that as an explicitly undefined border case,
+        #      to keep the codepath simpler & faster?
+
+        # if we can parse rounds parameter, check if it's w/in bounds.
+        if self._has_rounds_introspection:
+            # XXX: this might be a good place to use parsehash()
+            hash_obj = self.handler.from_string(hash)
+            try:
+                rounds = hash_obj.rounds
+            except AttributeError: # pragma: no cover -- sanity check
+                # XXX: all builtin hashes should have rounds attr,
+                #      so should a warning be issues here?
+                pass
+            else:
+                mn = self._min_rounds
+                if mn is not None and rounds < mn:
+                    return True
+                mx = self._max_rounds
+                if mx and rounds > mx:
+                    return True
+
+        return False
+
+    #===================================================================
+    # eoc
+    #===================================================================
+
+#=============================================================================
+# _CryptConfig helper class
+#=============================================================================
+class _CryptConfig(object):
+    """parses, validates, and stores CryptContext config
+
+    this is a helper used internally by CryptContext to handle
+    parsing, validation, and serialization of it's config options.
+    split out from the main class, but not made public since
+    that just complicates interface too much (c.f. CryptPolicy)
+
+    :arg source: config as dict mapping ``(cat,scheme,option) -> value``
+    """
+    #===================================================================
+    # instance attrs
+    #===================================================================
+
+    # triple-nested dict which maps scheme -> category -> key -> value,
+    # storing all hash-specific options
+    _scheme_options = None
+
+    # double-nested dict which maps key -> category -> value
+    # storing all CryptContext options
+    _context_options = None
+
+    # tuple of handler objects
+    handlers = None
+
+    # tuple of scheme objects in same order as handlers
+    schemes = None
+
+    # tuple of categories in alphabetical order (not including None)
+    categories = None
+
+    # dict mapping category -> default scheme
+    _default_schemes = None
+
+    # dict mapping (scheme, category) -> _CryptRecord
+    _records = None
+
+    # dict mapping category -> list of _CryptRecord instances for that category,
+    # in order of schemes(). populated on demand by _get_record_list()
+    _record_lists = None
+
+    #===================================================================
+    # constructor
+    #===================================================================
+    def __init__(self, source):
+        self._init_scheme_list(source.get((None,None,"schemes")))
+        self._init_options(source)
+        self._init_default_schemes()
+        self._init_records()
+
+    def _init_scheme_list(self, data):
+        """initialize .handlers and .schemes attributes"""
+        handlers  = []
+        schemes = []
+        if isinstance(data, str):
+            data = splitcomma(data)
+        for elem in data or ():
+            # resolve elem -> handler & scheme
+            if hasattr(elem, "name"):
+                handler = elem
+                scheme = handler.name
+                _validate_handler_name(scheme)
+            elif isinstance(elem, str):
+                handler = get_crypt_handler(elem)
+                scheme = handler.name
+            else:
+                raise TypeError("scheme must be name or CryptHandler, "
+                                "not %r" % type(elem))
+
+            # check scheme name isn't already in use
+            if scheme in schemes:
+                raise KeyError("multiple handlers with same name: %r" %
+                               (scheme,))
+
+            # add to handler list
+            handlers.append(handler)
+            schemes.append(scheme)
+
+        self.handlers = tuple(handlers)
+        self.schemes = tuple(schemes)
+
+    #===================================================================
+    # lowlevel options
+    #===================================================================
+
+    #---------------------------------------------------------------
+    # init lowlevel option storage
+    #---------------------------------------------------------------
+    def _init_options(self, source):
+        """load config dict into internal representation,
+        and init .categories attr
+        """
+        # prepare dicts & locals
+        norm_scheme_option = self._norm_scheme_option
+        norm_context_option = self._norm_context_option
+        self._scheme_options = scheme_options = {}
+        self._context_options = context_options = {}
+        categories = set()
+
+        # load source config into internal storage
+        for (cat, scheme, key), value in iteritems(source):
+            categories.add(cat)
+            if scheme:
+                # normalize scheme option
+                key, value = norm_scheme_option(key, value)
+
+                # store in scheme_options
+                # map structure: scheme_options[scheme][category][key] = value
+                try:
+                    category_map = scheme_options[scheme]
+                except KeyError:
+                    scheme_options[scheme] = {cat: {key: value}}
+                else:
+                    try:
+                        option_map = category_map[cat]
+                    except KeyError:
+                        category_map[cat] = {key: value}
+                    else:
+                        option_map[key] = value
+            else:
+                # normalize context option
+                if cat and key == "schemes":
+                    raise KeyError("'schemes' context option is not allowed "
+                                   "per category")
+                key, value = norm_context_option(key, value)
+
+                # store in context_options
+                # map structure: context_options[key][category] = value
+                try:
+                    category_map = context_options[key]
+                except KeyError:
+                    context_options[key] = {cat: value}
+                else:
+                    category_map[cat] = value
+
+        # store list of configured categories
+        categories.discard(None)
+        self.categories = tuple(sorted(categories))
+
+    def _norm_scheme_option(self, key, value):
+        # check for invalid options
+        if key == "rounds":
+            # for now, translating this to 'default_rounds' to be helpful.
+            # need to pick one of the two names as official,
+            # and deprecate the other one.
+            key = "default_rounds"
+        elif key in _forbidden_scheme_options:
+            raise KeyError("%r option not allowed in CryptContext "
+                           "configuration" % (key,))
+        # coerce strings for certain fields (e.g. min_rounds uses ints)
+        if isinstance(value, str):
+            func = _coerce_scheme_options.get(key)
+            if func:
+                value = func(value)
+        return key, value
+
+    def _norm_context_option(self, key, value):
+        schemes = self.schemes
+        if key == "default":
+            if hasattr(value, "name"):
+                value = value.name
+            elif not isinstance(value, str):
+                raise ExpectedTypeError(value, "str", "default")
+            if schemes and value not in schemes:
+                raise KeyError("default scheme not found in policy")
+        elif key == "deprecated":
+            if isinstance(value, str):
+                value = splitcomma(value)
+            elif not isinstance(value, (list,tuple)):
+                raise ExpectedTypeError(value, "str or seq", "deprecated")
+            if 'auto' in value:
+                if len(value) > 1:
+                    raise ValueError("cannot list other schemes if "
+                                     "``deprecated=['auto']`` is used")
+            elif schemes:
+                # make sure list of deprecated schemes is subset of configured schemes
+                for scheme in value:
+                    if not isinstance(scheme, str):
+                        raise ExpectedTypeError(value, "str", "deprecated element")
+                    if scheme not in schemes:
+                        raise KeyError("deprecated scheme not found "
+                                   "in policy: %r" % (scheme,))
+        elif key == "min_verify_time":
+            warn("'min_verify_time' is deprecated as of Passlib 1.6, will be "
+                 "ignored in 1.7, and removed in 1.8.", DeprecationWarning)
+            value = float(value)
+            if value < 0:
+                raise ValueError("'min_verify_time' must be >= 0")
+        elif key != "schemes":
+            raise KeyError("unknown CryptContext keyword: %r" % (key,))
+        return key, value
+
+    #---------------------------------------------------------------
+    # reading context options
+    #---------------------------------------------------------------
+    def get_context_optionmap(self, key, _default={}):
+        """return dict mapping category->value for specific context option.
+        (treat retval as readonly).
+        """
+        return self._context_options.get(key, _default)
+
+    def get_context_option_with_flag(self, category, key):
+        """return value of specific option, handling category inheritance.
+        also returns flag indicating whether value is category-specific.
+        """
+        try:
+            category_map = self._context_options[key]
+        except KeyError:
+            return None, False
+        value = category_map.get(None)
+        if category:
+            try:
+                alt = category_map[category]
+            except KeyError:
+                pass
+            else:
+                if value is None or alt != value:
+                    return alt, True
+        return value, False
+
+    #---------------------------------------------------------------
+    # reading scheme options
+    #---------------------------------------------------------------
+    def _get_scheme_optionmap(self, scheme, category, default={}):
+        """return all options for (scheme,category) combination
+        (treat return as readonly)
+        """
+        try:
+            return self._scheme_options[scheme][category]
+        except KeyError:
+            return default
+
+    def get_scheme_options_with_flag(self, scheme, category):
+        """return composite dict of all options set for scheme.
+        includes options inherited from 'all' and from default category.
+        result can be modified.
+        returns (kwds, has_cat_specific_options)
+        """
+        # start out with copy of global options
+        get_optionmap = self._get_scheme_optionmap
+        kwds = get_optionmap("all", None).copy()
+        has_cat_options = False
+
+        # add in category-specific global options
+        if category:
+            defkwds = kwds.copy() # <-- used to detect category-specific options
+            kwds.update(get_optionmap("all", category))
+
+        # add in default options for scheme
+        other = get_optionmap(scheme, None)
+        kwds.update(other)
+
+        # load category-specific options for scheme
+        if category:
+            defkwds.update(other)
+            kwds.update(get_optionmap(scheme, category))
+
+            # compare default category options to see if there's anything
+            # category-specific
+            if kwds != defkwds:
+                has_cat_options = True
+
+        return kwds, has_cat_options
+
+    #===================================================================
+    # deprecated & default schemes
+    #===================================================================
+    def _init_default_schemes(self):
+        """initialize maps containing default scheme for each category.
+
+        have to do this after _init_options(), since the default scheme
+        is affected by the list of deprecated schemes.
+        """
+        # init maps & locals
+        get_optionmap = self.get_context_optionmap
+        default_map = self._default_schemes = get_optionmap("default").copy()
+        dep_map = get_optionmap("deprecated")
+        schemes = self.schemes
+        if not schemes:
+            return
+
+        # figure out default scheme
+        deps = dep_map.get(None) or ()
+        default = default_map.get(None)
+        if not default:
+            for scheme in schemes:
+                if scheme not in deps:
+                    default_map[None] = scheme
+                    break
+            else:
+                raise ValueError("must have at least one non-deprecated scheme")
+        elif default in deps:
+            raise ValueError("default scheme cannot be deprecated")
+
+        # figure out per-category default schemes,
+        for cat in self.categories:
+            cdeps = dep_map.get(cat, deps)
+            cdefault = default_map.get(cat, default)
+            if not cdefault:
+                for scheme in schemes:
+                    if scheme not in cdeps:
+                        default_map[cat] = scheme
+                        break
+                else:
+                    raise ValueError("must have at least one non-deprecated "
+                                     "scheme for %r category" % cat)
+            elif cdefault in cdeps:
+                raise ValueError("default scheme for %r category "
+                                 "cannot be deprecated" % cat)
+
+    def default_scheme(self, category):
+        "return default scheme for specific category"
+        defaults = self._default_schemes
+        try:
+            return defaults[category]
+        except KeyError:
+            pass
+        if not self.schemes:
+            raise KeyError("no hash schemes configured for this "
+                           "CryptContext instance")
+        return defaults[None]
+
+    def is_deprecated_with_flag(self, scheme, category):
+        "is scheme deprecated under particular category?"
+        depmap = self.get_context_optionmap("deprecated")
+        def test(cat):
+            source = depmap.get(cat, depmap.get(None))
+            if source is None:
+                return None
+            elif 'auto' in source:
+                return scheme != self.default_scheme(cat)
+            else:
+                return scheme in source
+        value = test(None) or False
+        if category:
+            alt = test(category)
+            if alt is not None and value != alt:
+                return alt, True
+        return value, False
+
+    #===================================================================
+    # CryptRecord objects
+    #===================================================================
+    def _init_records(self):
+        # NOTE: this step handles final validation of settings,
+        #       checking for violatiions against handler's internal invariants.
+        #       this is why we create all the records now,
+        #       so CryptContext throws error immediately rather than later.
+        self._record_lists = {}
+        records = self._records = {}
+        get_options = self._get_record_options_with_flag
+        categories = self.categories
+        for handler in self.handlers:
+            scheme = handler.name
+            kwds, _ = get_options(scheme, None)
+            records[scheme, None] = _CryptRecord(handler, **kwds)
+            for cat in categories:
+                kwds, has_cat_options = get_options(scheme, cat)
+                if has_cat_options:
+                    records[scheme, cat] = _CryptRecord(handler, cat, **kwds)
+                # NOTE: if handler has no category-specific opts, get_record()
+                # will automatically use the default category's record.
+        # NOTE: default records for specific category stored under the
+        # key (None,category); these are populated on-demand by get_record().
+
+    def _get_record_options_with_flag(self, scheme, category):
+        """return composite dict of options for given scheme + category.
+
+        this is currently a private method, though some variant
+        of it's output may eventually be made public.
+
+        given a scheme & category, it returns two things:
+        a set of all the keyword options to pass to the _CryptRecord constructor,
+        and a bool flag indicating whether any of these options
+        were specific to the named category. if this flag is false,
+        the options are identical to the options for the default category.
+
+        the options dict includes all the scheme-specific settings,
+        as well as optional *deprecated* and *min_verify_time* keywords.
+        """
+        # get scheme options
+        kwds, has_cat_options = self.get_scheme_options_with_flag(scheme, category)
+
+        # throw in deprecated flag
+        value, not_inherited = self.is_deprecated_with_flag(scheme, category)
+        if value:
+            kwds['deprecated'] = True
+        if not_inherited:
+            has_cat_options = True
+
+        # add in min_verify_time setting from context
+        value, not_inherited = self.get_context_option_with_flag(category, "min_verify_time")
+        if value:
+            kwds['min_verify_time'] = value
+        if not_inherited:
+            has_cat_options = True
+
+        return kwds, has_cat_options
+
+    def get_record(self, scheme, category):
+        "return record for specific scheme & category (cached)"
+        # NOTE: this is part of the critical path shared by
+        #       all of CryptContext's PasswordHash methods,
+        #       hence all the caching and error checking.
+
+        # quick lookup in cache
+        try:
+            return self._records[scheme, category]
+        except KeyError:
+            pass
+
+        # type check
+        if category is not None and not isinstance(category, str):
+            if PY2 and isinstance(category, unicode):
+                # for compatibility with unicode-centric py2 apps
+                return self.get_record(scheme, category.encode("utf-8"))
+            raise ExpectedTypeError(category, "str or None", "category")
+        if scheme is not None and not isinstance(scheme, str):
+            raise ExpectedTypeError(scheme, "str or None", "scheme")
+
+        # if scheme=None,
+        # use record for category's default scheme, and cache result.
+        if not scheme:
+            default = self.default_scheme(category)
+            assert default
+            record = self._records[None, category] = self.get_record(default,
+                                                                      category)
+            return record
+
+        # if no record for (scheme, category),
+        # use record for (scheme, None), and cache result.
+        if category:
+            try:
+                cache = self._records
+                record = cache[scheme, category] = cache[scheme, None]
+                return record
+            except KeyError:
+                pass
+
+        # scheme not found in configuration for default category
+        raise KeyError("crypt algorithm not found in policy: %r" % (scheme,))
+
+    def _get_record_list(self, category=None):
+        """return list of records for category (cached)
+
+        this is an internal helper used only by identify_record()
+        """
+        # type check of category - handled by _get_record()
+        # quick lookup in cache
+        try:
+            return self._record_lists[category]
+        except KeyError:
+            pass
+        # cache miss - build list from scratch
+        value = self._record_lists[category] = [
+            self.get_record(scheme, category)
+            for scheme in self.schemes
+            ]
+        return value
+
+    def identify_record(self, hash, category, required=True):
+        """internal helper to identify appropriate _CryptRecord for hash"""
+        # NOTE: this is part of the critical path shared by
+        #       all of CryptContext's PasswordHash methods,
+        #       hence all the caching and error checking.
+        # FIXME: if multiple hashes could match (e.g. lmhash vs nthash)
+        #        this will only return first match. might want to do something
+        #        about this in future, but for now only hashes with
+        #        unique identifiers will work properly in a CryptContext.
+        # XXX: if all handlers have a unique prefix (e.g. all are MCF / LDAP),
+        #      could use dict-lookup to speed up this search.
+        if not isinstance(hash, base_string_types):
+            raise ExpectedStringError(hash, "hash")
+        # type check of category - handled by _get_record_list()
+        for record in self._get_record_list(category):
+            if record.identify(hash):
+                return record
+        if not required:
+            return None
+        elif not self.schemes:
+            raise KeyError("no crypt algorithms supported")
+        else:
+            raise ValueError("hash could not be identified")
+
+    #===================================================================
+    # serialization
+    #===================================================================
+    def iter_config(self, resolve=False):
+        """regenerate original config.
+
+        this is an iterator which yields ``(cat,scheme,option),value`` items,
+        in the order they generally appear inside an INI file.
+        if interpreted as a dictionary, it should match the original
+        keywords passed to the CryptContext (aside from any canonization).
+
+        it's mainly used as the internal backend for most of the public
+        serialization methods.
+        """
+        # grab various bits of data
+        scheme_options = self._scheme_options
+        context_options = self._context_options
+        scheme_keys = sorted(scheme_options)
+        context_keys = sorted(context_options)
+
+        # write loaded schemes (may differ from 'schemes' local var)
+        if 'schemes' in context_keys:
+            context_keys.remove("schemes")
+        value = self.handlers if resolve else self.schemes
+        if value:
+            yield (None, None, "schemes"), list(value)
+
+        # then run through config for each user category
+        for cat in (None,) + self.categories:
+
+            # write context options
+            for key in context_keys:
+                try:
+                    value = context_options[key][cat]
+                except KeyError:
+                    pass
+                else:
+                    if isinstance(value, list):
+                        value = list(value)
+                    yield (cat, None, key), value
+
+            # write per-scheme options for all schemes.
+            for scheme in scheme_keys:
+                try:
+                    kwds = scheme_options[scheme][cat]
+                except KeyError:
+                    pass
+                else:
+                    for key in sorted(kwds):
+                        yield (cat, scheme, key), kwds[key]
+
+    #===================================================================
+    # eoc
+    #===================================================================
+
+#=============================================================================
+# main CryptContext class
+#=============================================================================
+class CryptContext(object):
+    """Helper for encrypting passwords using different algorithms.
+
+    Instances of this class allow applications to choose a specific
+    set of hash algorithms which they wish to support, set limits and defaults
+    for the rounds and salt sizes those algorithms should use, flag
+    which algorithms should be deprecated, and automatically handle
+    migrating users to stronger hashes when they log in.
+
+    Basic usage::
+
+        >>> ctx = CryptContext(schemes=[...])
+
+    See the Passlib online documentation for details and full documentation.
+    """
+    # FIXME: altering the configuration of this object isn't threadsafe,
+    # but is generally only done during application init, so not a major
+    # issue (just yet).
+
+    # XXX: would like some way to restrict the categories that are allowed,
+    # to restrict what the app OR the config can use.
+
+    #===================================================================
+    # instance attrs
+    #===================================================================
+
+    # _CryptConfig instance holding current parsed config
+    _config = None
+
+    # copy of _config methods, stored in CryptContext instance for speed.
+    _get_record = None
+    _identify_record = None
+
+    #===================================================================
+    # secondary constructors
+    #===================================================================
+    @classmethod
+    def _norm_source(cls, source):
+        "internal helper - accepts string, dict, or context"
+        if isinstance(source, dict):
+            return cls(**source)
+        elif isinstance(source, cls):
+            return source
+        else:
+            self = cls()
+            self.load(source)
+            return self
+
+    @classmethod
+    def from_string(cls, source, section="passlib", encoding="utf-8"):
+        """create new CryptContext instance from an INI-formatted string.
+
+        :type source: unicode or bytes
+        :arg source:
+            string containing INI-formatted content.
+
+        :type section: str
+        :param section:
+            option name of section to read from, defaults to ``"passlib"``.
+
+        :type encoding: str
+        :arg encoding:
+            optional encoding used when source is bytes, defaults to ``"utf-8"``.
+
+        :returns:
+            new :class:`CryptContext` instance, configured based on the
+            parameters in the *source* string.
+
+        Usage example::
+
+            >>> from passlib.context import CryptContext
+            >>> context = CryptContext.from_string('''
+            ... [passlib]
+            ... schemes = sha256_crypt, des_crypt
+            ... sha256_crypt__default_rounds = 30000
+            ... ''')
+
+        .. versionadded:: 1.6
+
+        .. seealso:: :meth:`to_string`, the inverse of this constructor.
+        """
+        if not isinstance(source, base_string_types):
+            raise ExpectedTypeError(source, "unicode or bytes", "source")
+        self = cls(_autoload=False)
+        self.load(source, section=section, encoding=encoding)
+        return self
+
+    @classmethod
+    def from_path(cls, path, section="passlib", encoding="utf-8"):
+        """create new CryptContext instance from an INI-formatted file.
+
+        this functions exactly the same as :meth:`from_string`,
+        except that it loads from a local file.
+
+        :type path: str
+        :arg path:
+            path to local file containing INI-formatted config.
+
+        :type section: str
+        :param section:
+            option name of section to read from, defaults to ``"passlib"``.
+
+        :type encoding: str
+        :arg encoding:
+            encoding used to load file, defaults to ``"utf-8"``.
+
+        :returns:
+            new CryptContext instance, configured based on the parameters
+            stored in the file *path*.
+
+        .. versionadded:: 1.6
+
+        .. seealso:: :meth:`from_string` for an equivalent usage example.
+        """
+        self = cls(_autoload=False)
+        self.load_path(path, section=section, encoding=encoding)
+        return self
+
+    def copy(self, **kwds):
+        """Return copy of existing CryptContext instance.
+
+        This function returns a new CryptContext instance whose configuration
+        is exactly the same as the original, with the exception that any keywords
+        passed in will take precedence over the original settings.
+        As an example::
+
+            >>> from passlib.context import CryptContext
+
+            >>> # given an existing context...
+            >>> ctx1 = CryptContext(["sha256_crypt", "md5_crypt"])
+
+            >>> # copy can be used to make a clone, and update
+            >>> # some of the settings at the same time...
+            >>> ctx2 = custom_app_context.copy(default="md5_crypt")
+
+            >>> # and the original will be unaffected by the change
+            >>> ctx1.default_scheme()
+            "sha256_crypt"
+            >>> ctx2.default_scheme()
+            "md5_crypt"
+
+        .. versionchanged:: 1.6
+            This method was previously named :meth:`!replace`. That alias
+            has been deprecated, and will be removed in Passlib 1.8.
+
+        .. seealso:: :meth:`update`
+        """
+        # XXX: it would be faster to store ref to self._config,
+        #      but don't want to share config objects til sure
+        #      can rely on them being immutable.
+        other = CryptContext(_autoload=False)
+        other.load(self)
+        if kwds:
+            other.load(kwds, update=True)
+        return other
+
+    def replace(self, **kwds):
+        "deprecated alias of :meth:`copy`"
+        warn("CryptContext().replace() has been deprecated in Passlib 1.6, "
+             "and will be removed in Passlib 1.8, "
+             "it has been renamed to CryptContext().copy()",
+             DeprecationWarning, stacklevel=2)
+        return self.copy(**kwds)
+
+    #===================================================================
+    # init
+    #===================================================================
+    def __init__(self, schemes=None,
+                 # keyword only...
+                 policy=_UNSET, # <-- deprecated
+                 _autoload=True, **kwds):
+        # XXX: add ability to make flag certain contexts as immutable,
+        #      e.g. the builtin passlib ones?
+        # XXX: add a name or import path for the contexts, to help out repr?
+        if schemes is not None:
+            kwds['schemes'] = schemes
+        if policy is not _UNSET:
+            warn("The CryptContext ``policy`` keyword has been deprecated as of Passlib 1.6, "
+                 "and will be removed in Passlib 1.8; please use "
+                 "``CryptContext.from_string()` or "
+                 "``CryptContext.from_path()`` instead.",
+                 DeprecationWarning)
+            if policy is None:
+                self.load(kwds)
+            elif isinstance(policy, CryptPolicy):
+                self.load(policy._context)
+                self.update(kwds)
+            else:
+                raise TypeError("policy must be a CryptPolicy instance")
+        elif _autoload:
+            self.load(kwds)
+        else:
+            assert not kwds, "_autoload=False and kwds are mutually exclusive"
+
+    # XXX: would this be useful?
+    ##def __str__(self):
+    ##    if PY3:
+    ##        return self.to_string()
+    ##    else:
+    ##        return self.to_string().encode("utf-8")
+
+    def __repr__(self):
+        return "<CryptContext at 0x%0x>" % id(self)
+
+    #===================================================================
+    # deprecated policy object
+    #===================================================================
+    def _get_policy(self):
+        # The CryptPolicy class has been deprecated, so to support any
+        # legacy accesses, we create a stub policy object so .policy attr
+        # will continue to work.
+        #
+        # the code waits until app accesses a specific policy object attribute
+        # before issuing deprecation warning, so developer gets method-specific
+        # suggestion for how to upgrade.
+
+        # NOTE: making a copy of the context so the policy acts like a snapshot,
+        # to retain the pre-1.6 behavior.
+        return CryptPolicy(_internal_context=self.copy(), _stub_policy=True)
+
+    def _set_policy(self, policy):
+        warn("The CryptPolicy class and the ``context.policy`` attribute have "
+             "been deprecated as of Passlib 1.6, and will be removed in "
+             "Passlib 1.8; please use the ``context.load()`` and "
+             "``context.update()`` methods instead.",
+             DeprecationWarning, stacklevel=2)
+        if isinstance(policy, CryptPolicy):
+            self.load(policy._context)
+        else:
+            raise TypeError("expected CryptPolicy instance")
+
+    policy = property(_get_policy, _set_policy,
+                    doc="[deprecated] returns CryptPolicy instance "
+                        "tied to this CryptContext")
+
+    #===================================================================
+    # loading / updating configuration
+    #===================================================================
+    @staticmethod
+    def _parse_ini_stream(stream, section, filename):
+        "helper read INI from stream, extract passlib section as dict"
+        # NOTE: this expects a unicode stream under py3,
+        # and a utf-8 bytes stream under py2,
+        # allowing the resulting dict to always use native strings.
+        p = SafeConfigParser()
+        if PY_MIN_32:
+            # python 3.2 deprecated readfp in favor of read_file
+            p.read_file(stream, filename)
+        else:
+            p.readfp(stream, filename)
+        return dict(p.items(section))
+
+    def load_path(self, path, update=False, section="passlib", encoding="utf-8"):
+        """Load new configuration into CryptContext from a local file.
+
+        This function is a wrapper for :meth:`load`, which
+        loads a configuration string from the local file *path*,
+        instead of an in-memory source. It's behavior and options
+        are otherwise identical to :meth:`!load` when provided with
+        an INI-formatted string.
+
+        .. versionadded:: 1.6
+        """
+        def helper(stream):
+            kwds = self._parse_ini_stream(stream, section, path)
+            return self.load(kwds, update=update)
+        if PY3:
+            # decode to unicode, which load() expected under py3
+            with open(path, "rt", encoding=encoding) as stream:
+                return helper(stream)
+        elif encoding in ["utf-8", "ascii"]:
+            # keep as utf-8 bytes, which load() expects under py2
+            with open(path, "rb") as stream:
+                return helper(stream)
+        else:
+            # transcode to utf-8 bytes
+            with open(path, "rb") as fh:
+                tmp = fh.read().decode(encoding).encode("utf-8")
+                return helper(BytesIO(tmp))
+
+    def load(self, source, update=False, section="passlib", encoding="utf-8"):
+        """Load new configuration into CryptContext, replacing existing config.
+
+        :arg source:
+            source of new configuration to load.
+            this value can be a number of different types:
+
+            * a :class:`!dict` object, or compatible Mapping
+
+                the key/value pairs will be interpreted the same
+                keywords for the :class:`CryptContext` class constructor.
+
+            * a :class:`!unicode` or :class:`!bytes` string
+
+                this will be interpreted as an INI-formatted file,
+                and appropriate key/value pairs will be loaded from
+                the specified *section*.
+
+            * another :class:`!CryptContext` object.
+
+                this will export a snapshot of it's configuration
+                using :meth:`to_dict`.
+
+        :type update: bool
+        :param update:
+            By default, :meth:`load` will replace the existing configuration
+            entirely. If ``update=True``, it will preserve any existing
+            configuration options that are not overridden by the new source,
+            much like the :meth:`update` method.
+
+        :type section: str
+        :param section:
+            When parsing an INI-formatted string, :meth:`load` will look for
+            a section named ``"passlib"``. This option allows an alternate
+            section name to be used. Ignored when loading from a dictionary.
+
+        :type encoding: str
+        :param encoding:
+            Encoding to use when decode bytes from string.
+            Defaults to ``"utf-8"``. Ignoring when loading from a dictionary.
+
+        :raises TypeError:
+            * If the source cannot be identified.
+            * If an unknown / malformed keyword is encountered.
+
+        :raises ValueError:
+            If an invalid keyword value is encountered.
+
+        .. note::
+
+            If an error occurs during a :meth:`!load` call, the :class`!CryptContext`
+            instance will be restored to the configuration it was in before
+            the :meth:`!load` call was made; this is to ensure it is
+            *never* left in an inconsistent state due to a load error.
+
+        .. versionadded:: 1.6
+        """
+        #-----------------------------------------------------------
+        # autodetect source type, convert to dict
+        #-----------------------------------------------------------
+        parse_keys = True
+        if isinstance(source, base_string_types):
+            if PY3:
+                source = to_unicode(source, encoding, param="source")
+            else:
+                source = to_bytes(source, "utf-8", source_encoding=encoding,
+                                  param="source")
+            source = self._parse_ini_stream(NativeStringIO(source), section,
+                                            "<string>")
+        elif isinstance(source, CryptContext):
+            # extract dict directly from config, so it can be merged later
+            source = dict(source._config.iter_config(resolve=True))
+            parse_keys = False
+        elif not hasattr(source, "items"):
+            # mappings are left alone, otherwise throw an error.
+            raise ExpectedTypeError(source, "string or dict", "source")
+
+        # XXX: add support for other iterable types, e.g. sequence of pairs?
+
+        #-----------------------------------------------------------
+        # parse dict keys into (category, scheme, option) format,
+        # merge with existing configuration if needed
+        #-----------------------------------------------------------
+        if parse_keys:
+            parse = self._parse_config_key
+            source = dict((parse(key), value)
+                          for key, value in iteritems(source))
+        if update and self._config is not None:
+            # if updating, do nothing if source is empty,
+            if not source:
+                return
+            # otherwise overlay source on top of existing config
+            tmp = source
+            source = dict(self._config.iter_config(resolve=True))
+            source.update(tmp)
+
+        #-----------------------------------------------------------
+        # compile into _CryptConfig instance, and update state
+        #-----------------------------------------------------------
+        config = _CryptConfig(source)
+        self._config = config
+        self._get_record = config.get_record
+        self._identify_record = config.identify_record
+
+    @staticmethod
+    def _parse_config_key(ckey):
+        """helper used to parse ``cat__scheme__option`` keys into a tuple"""
+        # split string into 1-3 parts
+        assert isinstance(ckey, str)
+        parts = ckey.replace(".","__").split("__")
+        count = len(parts)
+        if count == 1:
+            cat, scheme, key = None, None, parts[0]
+        elif count == 2:
+            cat = None
+            scheme, key = parts
+        elif count == 3:
+            cat, scheme, key = parts
+        else:
+            raise TypeError("keys must have less than 3 separators: %r" %
+                            (ckey,))
+        # validate & normalize the parts
+        if cat == "default":
+            cat = None
+        elif not cat and cat is not None:
+            raise TypeError("empty category: %r" % ckey)
+        if scheme == "context":
+            scheme = None
+        elif not scheme and scheme is not None:
+            raise TypeError("empty scheme: %r" % ckey)
+        if not key:
+            raise TypeError("empty option: %r" % ckey)
+        return cat, scheme, key
+
+    def update(self, *args, **kwds):
+        """Helper for quickly changing configuration.
+
+        This acts much like the :meth:`!dict.update` method:
+        it updates the context's configuration,
+        replacing the original value(s) for the specified keys,
+        and preserving the rest.
+        It accepts any :ref:`keyword <context-options>`
+        accepted by the :class:`!CryptContext` constructor.
+
+        .. versionadded:: 1.6
+
+        .. seealso:: :meth:`copy`
+        """
+        if args:
+            if len(args) > 1:
+                raise TypeError("expected at most one positional argument")
+            if kwds:
+                raise TypeError("positional arg and keywords mutually exclusive")
+            self.load(args[0], update=True)
+        elif kwds:
+            self.load(kwds, update=True)
+
+    # XXX: make this public? even just as flag to load?
+    # FIXME: this function suffered some bitrot in 1.6.1,
+    #        will need to be updated before works again.
+    ##def _simplify(self):
+    ##    "helper to remove redundant/unused options"
+    ##    # don't do anything if no schemes are defined
+    ##    if not self._schemes:
+    ##        return
+    ##
+    ##    def strip_items(target, filter):
+    ##        keys = [key for key,value in iteritems(target)
+    ##                if filter(key,value)]
+    ##        for key in keys:
+    ##            del target[key]
+    ##
+    ##    # remove redundant default.
+    ##    defaults = self._default_schemes
+    ##    if defaults.get(None) == self._schemes[0]:
+    ##        del defaults[None]
+    ##
+    ##    # remove options for unused schemes.
+    ##    scheme_options = self._scheme_options
+    ##    schemes = self._schemes + ("all",)
+    ##    strip_items(scheme_options, lambda k,v: k not in schemes)
+    ##
+    ##    # remove rendundant cat defaults.
+    ##    cur = self.default_scheme()
+    ##    strip_items(defaults, lambda k,v: k and v==cur)
+    ##
+    ##    # remove redundant category deprecations.
+    ##    # TODO: this should work w/ 'auto', but needs closer inspection
+    ##    deprecated = self._deprecated_schemes
+    ##    cur = self._deprecated_schemes.get(None)
+    ##    strip_items(deprecated, lambda k,v: k and v==cur)
+    ##
+    ##    # remove redundant category options.
+    ##    for scheme, config in iteritems(scheme_options):
+    ##        if None in config:
+    ##            cur = config[None]
+    ##            strip_items(config, lambda k,v: k and v==cur)
+    ##
+    ##    # XXX: anything else?
+
+    #===================================================================
+    # reading configuration
+    #===================================================================
+    def schemes(self, resolve=False):
+        """return schemes loaded into this CryptContext instance.
+
+        :type resolve: bool
+        :arg resolve:
+            if ``True``, will return a tuple of :class:`~passlib.ifc.PasswordHash`
+            objects instead of their names.
+
+        :returns:
+            returns tuple of the schemes configured for this context
+            via the *schemes* option.
+
+        .. versionadded:: 1.6
+            This was previously available as ``CryptContext().policy.schemes()``
+
+        .. seealso:: the :ref:`schemes <context-schemes-option>` option for usage example.
+        """
+        return self._config.handlers if resolve else self._config.schemes
+
+    # XXX: need to decide if exposing this would be useful to applications
+    #      in any way that isn't already served by to_dict();
+    #      and then decide whether to expose ability as deprecated_schemes(),
+    #      is_deprecated(), or a just add a schemes(deprecated=True) flag.
+    def _is_deprecated_scheme(self, scheme, category=None):
+        "helper used by unittests to check if scheme is deprecated"
+        return self._get_record(scheme, category).deprecated
+
+    def default_scheme(self, category=None, resolve=False):
+        """return name of scheme that :meth:`encrypt` will use by default.
+
+        :type resolve: bool
+        :arg resolve:
+            if ``True``, will return a :class:`~passlib.ifc.PasswordHash`
+            object instead of the name.
+
+        :type category: str or None
+        :param category:
+            Optional :ref:`user category <user-categories>`.
+            If specified, this will return the catgory-specific default scheme instead.
+
+        :returns:
+            name of the default scheme.
+
+        .. seealso:: the :ref:`default <context-default-option>` option for usage example.
+
+        .. versionadded:: 1.6
+        """
+        # type check of category - handled by _get_record()
+        record = self._get_record(None, category)
+        return record.handler if resolve else record.scheme
+
+    # XXX: need to decide if exposing this would be useful in any way
+    ##def categories(self):
+    ##    """return user-categories with algorithm-specific options in this CryptContext.
+    ##
+    ##    this will always return a tuple.
+    ##    if no categories besides the default category have been configured,
+    ##    the tuple will be empty.
+    ##    """
+    ##    return self._config.categories
+
+    def handler(self, scheme=None, category=None):
+        """helper to resolve name of scheme -> :class:`~passlib.ifc.PasswordHash` object used by scheme.
+
+        :arg scheme: