changeset 1243:20625402725c namespaces

merged default branch into namespaces branch
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 28 Jan 2012 17:42:01 +0100
parents 064024ea500b (current diff) fca26a1acf26 (diff)
children 83d17454aebb
files MoinMoin/apps/admin/views.py MoinMoin/apps/feed/views.py MoinMoin/apps/frontend/views.py MoinMoin/config/default.py MoinMoin/items/__init__.py MoinMoin/items/_tests/test_Item.py MoinMoin/script/account/create.py MoinMoin/templates/global_history.html MoinMoin/templates/history.html MoinMoin/templates/layout.html MoinMoin/templates/usersettings.html MoinMoin/templates/usersettings_forms.html MoinMoin/themes/__init__.py MoinMoin/translations/MoinMoin.pot MoinMoin/user.py wikiconfig.py
diffstat 105 files changed, 3890 insertions(+), 1084 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/test_wikiutil.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/_tests/test_wikiutil.py	Sat Jan 28 17:42:01 2012 +0100
@@ -184,16 +184,16 @@
         result = wikiutil.getUnicodeIndexGroup('')
 
 def testis_URL():
-    sample_schemas = ['http', 'https', 'ftp', 'ssh']
-    for schema in sample_schemas:
-        result = wikiutil.is_URL(schema + ':MoinMoin')
+    sample_schemes = ['http', 'https', 'ftp', 'ssh']
+    for scheme in sample_schemes:
+        result = wikiutil.is_URL(scheme + ':MoinMoin')
         assert result
 
     # arg without ':' which is a mandatory requirement
     result = wikiutil.is_URL('MoinMoin')
     assert not result
-    # invalid schema
-    result = wikiutil.is_URL('invalid_schema:MoinMoin')
+    # invalid scheme
+    result = wikiutil.is_URL('invalid_scheme:MoinMoin')
     assert not result
 
 def testcontainsConflictMarker():
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/apps/admin/_tests/test_admin.py	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,38 @@
+# Copyright: 2011 Sam Toyer
+# License: GNU GPL v2 (or any later version), see LICENSE.txt for details
+
+"""
+    MoinMoin - admin view tests
+"""
+
+from flask import url_for
+
+class TestAdmin(object):
+    def _test_view_get(self, url, status='200 OK', data=('<html>', '</html>')):
+        with self.app.test_client() as c:
+            rv = c.get(url)
+            assert rv.status == status
+            assert rv.headers['Content-Type'] == 'text/html; charset=utf-8'
+            for item in data: assert item in rv.data
+
+    def test_index(self):
+        self._test_view_get(url_for('admin.index'))
+
+    def test_userprofile(self):
+        self._test_view_get(url_for('admin.userprofile', user_name='DoesntExist'), status='403 FORBIDDEN')
+
+    def test_wikiconfig(self):
+        self._test_view_get(url_for('admin.wikiconfig'), status='403 FORBIDDEN')
+
+    def test_wikiconfighelp(self):
+        self._test_view_get(url_for('admin.wikiconfighelp'), status='403 FORBIDDEN')
+
+    def test_interwikihelp(self):
+        self._test_view_get(url_for('admin.interwikihelp'))
+
+    def test_highlighterhelp(self):
+        self._test_view_get(url_for('admin.highlighterhelp'))
+
+    def test_itemsize(self):
+        self._test_view_get(url_for('admin.itemsize'))
+
--- a/MoinMoin/apps/admin/templates/admin/userbrowser.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/admin/templates/admin/userbrowser.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,6 +1,6 @@
 {% extends theme("layout.html") %}
 {% block content %}
-    <table>
+    <table class="zebra">
     <tr>
         <th>{{ _("User name") }}</th>
         <th>{{ _("Member of Groups") }}</th>
--- a/MoinMoin/apps/admin/templates/admin/wikiconfig.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/admin/templates/admin/wikiconfig.html	Sat Jan 28 17:42:01 2012 +0100
@@ -8,11 +8,11 @@
      "were removed from Moin.")
 }}
 </p>
-<table>
+<table class="zebra">
 <thead>
 <tr>
-<td><strong>{{ _('Variable name') }}</strong></td>
-<td><strong>{{ _('Setting') }}</strong></td>
+<th>{{ _('Variable name') }}</th>
+<th>{{ _('Setting') }}</th>
 </tr>
 </thead>
 <tbody>
--- a/MoinMoin/apps/admin/templates/admin/wikiconfighelp.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/admin/templates/admin/wikiconfighelp.html	Sat Jan 28 17:42:01 2012 +0100
@@ -6,12 +6,12 @@
     {% if desc %}
         <p>{{ desc }}</p>
     {% endif %}
-    <table>
+    <table class="zebra">
     <thead>
     <tr>
-        <td><strong>{{ _('Variable name') }}</strong></td>
-        <td><strong>{{ _('Default') }}</strong></td>
-        <td><strong>{{ _('Description') }}</strong></td>
+        <th>{{ _('Variable name') }}</th>
+        <th>{{ _('Default') }}</th>
+        <th>{{ _('Description') }}</th>
     </tr>
     </thead>
     <tbody>
--- a/MoinMoin/apps/admin/views.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/admin/views.py	Sat Jan 28 17:42:01 2012 +0100
@@ -37,14 +37,14 @@
     User Account Browser
     """
     groups = flaskg.groups
-    docs = user.search_users() # all users
-    user_accounts = [dict(uid=doc[ITEMID],
-                          name=doc[NAME],
-                          email=doc[EMAIL],
+    revs = user.search_users() # all users
+    user_accounts = [dict(uid=rev.meta[ITEMID],
+                          name=rev.meta[NAME],
+                          email=u'', # rev.meta[EMAIL],  # TODO: fix KeyError
                           disabled=False,  # TODO: add to index
-                          groups=[groupname for groupname in groups if doc[NAME] in groups[groupname]],
+                          groups=[groupname for groupname in groups if rev.meta[NAME] in groups[groupname]],
                      )
-                     for doc in docs]
+                     for rev in revs]
     return render_template('admin/userbrowser.html', user_accounts=user_accounts, title_name=_(u"User Browser"))
 
 
--- a/MoinMoin/apps/feed/_tests/test_feed.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/feed/_tests/test_feed.py	Sat Jan 28 17:42:01 2012 +0100
@@ -5,11 +5,11 @@
 MoinMoin - basic tests for feeds
 """
 
-from MoinMoin._tests import wikiconfig
+from flask import url_for
 
 from MoinMoin.items import Item
 from MoinMoin.config import CONTENTTYPE, COMMENT
-from MoinMoin._tests import update_item
+from MoinMoin._tests import update_item, wikiconfig
 
 class TestFeeds(object):
     class Config(wikiconfig.Config):
@@ -19,7 +19,7 @@
 
     def test_global_atom(self):
         with self.app.test_client() as c:
-            rv = c.get('/+feed/atom')
+            rv = c.get(url_for('feed.atom'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'application/atom+xml'
             assert rv.data.startswith('<?xml')
@@ -30,7 +30,7 @@
         basename = u'Foo'
         item = update_item(basename, {COMMENT: u"foo data for feed item"}, '')
         with self.app.test_client() as c:
-            rv = c.get('/+feed/atom')
+            rv = c.get(url_for('feed.atom'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'application/atom+xml'
             assert rv.data.startswith('<?xml')
@@ -39,7 +39,7 @@
         # tests the cache invalidation
         update_item(basename, {COMMENT: u"checking if the cache invalidation works"}, '')
         with self.app.test_client() as c:
-            rv = c.get('/+feed/atom')
+            rv = c.get(url_for('feed.atom'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'application/atom+xml'
             assert rv.data.startswith('<?xml')
--- a/MoinMoin/apps/feed/views.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/feed/views.py	Sat Jan 28 17:42:01 2012 +0100
@@ -14,8 +14,10 @@
 from flask import request, Response
 from flask import current_app as app
 from flask import g as flaskg
+from flask import url_for
 
 from werkzeug.contrib.atom import AtomFeed
+from jinja2 import Markup
 
 from whoosh.query import Term, And
 
@@ -28,7 +30,7 @@
 from MoinMoin.config import (NAME, NAME_EXACT, WIKINAME, ACL, ACTION, ADDRESS,
                             HOSTNAME, USERID, COMMENT, MTIME, REVID, ALL_REVS,
                             PARENTID, LATEST_REVS)
-from MoinMoin.themes import get_editor_info
+from MoinMoin.themes import get_editor_info, render_template
 from MoinMoin.items import Item
 from MoinMoin.util.crypto import cache_key
 from MoinMoin.util.interwiki import url_for_item
@@ -36,11 +38,12 @@
 @feed.route('/atom/<itemname:item_name>')
 @feed.route('/atom', defaults=dict(item_name=''))
 def atom(item_name):
-    # maybe we need different modes:
-    # - diffs in html don't look great without stylesheet
-    # - full item in html is nice
-    # - diffs in textmode are OK, but look very simple
-    # - full-item content in textmode is OK, but looks very simple
+    # Currently atom feeds behave in the fol. way
+    # - Text diffs are shown in a side-by-side fashion
+    # - The current binary item is fully rendered in the feed
+    # - Image(binary)'s diff is shown using PIL
+    # - First item is always rendered fully
+    # - Revision meta(id, size and comment) is shown for parent and current revision
     query = Term(WIKINAME, app.cfg.interwikiname)
     if item_name:
         query = And([query, Term(NAME_EXACT, item_name), ])
@@ -53,7 +56,10 @@
         content = None
         cid = None
     if content is None:
-        title = app.cfg.sitename
+        if not item_name:
+            title = u"{0}".format(app.cfg.sitename)
+        else:
+            title = u"{0} - {1}".format(app.cfg.sitename, item_name)
         feed = AtomFeed(title=title, feed_url=request.url, url=request.host_url)
         query = Term(WIKINAME, app.cfg.interwikiname)
         if item_name:
@@ -68,26 +74,40 @@
             try:
                 hl_item = Item.create(name, rev_id=this_revid)
                 if previous_revid is not None:
-                    # simple text diff for changes
+                    # HTML diff for subsequent revisions
                     previous_rev = item[previous_revid]
-                    content = hl_item._render_data_diff_text(previous_rev.data, this_rev.data)
-                    content = '<div><pre>{0}</pre></div>'.format(content)
+                    content = hl_item._render_data_diff_atom(previous_rev, this_rev)
                 else:
                     # full html rendering for new items
-                    content = hl_item._render_data()
-                content_type = 'xhtml'
+                    content = render_template('atom.html', get='first_revision', rev=this_rev, content=Markup(hl_item._render_data()), revision=this_revid)
+                content_type = 'html'
             except Exception as e:
                 logging.exception("content rendering crashed")
                 content = _(u'MoinMoin feels unhappy.')
                 content_type = 'text'
-            feed.add(title=name, title_type='text',
-                     summary=rev.meta.get(COMMENT, ''), summary_type='text',
-                     content=content, content_type=content_type,
-                     author=get_editor_info(rev.meta, external=True),
+            author = get_editor_info(rev.meta, external=True)
+            rev_comment = rev.meta.get(COMMENT, '')
+            if rev_comment:
+                # Trim down extremely long revision comment
+                if len(rev_comment) > 80:
+                    content = render_template('atom.html', get='comment_cont_merge', comment=rev_comment[79:], content=Markup(content))
+                    rev_comment = u"{0}...".format(rev_comment[:79])
+                feed_title = u"{0} - {1}".format(author.get(NAME, ''), rev_comment)
+            else:
+                feed_title = u"{0}".format(author.get(NAME, ''))
+            if not item_name:
+                feed_title = u"{0} - {1}".format(name, feed_title)
+            feed.add(title=feed_title, title_type='text',
+                     summary=content, summary_type=content_type,
+                     author=author,
                      url=url_for_item(name, rev=this_revid, _external=True),
                      updated=datetime.fromtimestamp(rev.meta[MTIME]),
                     )
         content = feed.to_string()
+        # Hack to add XSLT stylesheet declaration since AtomFeed doesn't allow this
+        content = content.split("\n")
+        content.insert(1, render_template('atom.html', get='xml'))
+        content = "\n".join(content)
         if cid is not None:
             app.cache.set(cid, content)
     return Response(content, content_type='application/atom+xml')
--- a/MoinMoin/apps/frontend/_tests/test_frontend.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/frontend/_tests/test_frontend.py	Sat Jan 28 17:42:01 2012 +0100
@@ -6,66 +6,204 @@
     MoinMoin - basic tests for frontend
 """
 
+from StringIO import StringIO
+
+import pytest
+
+from flask import url_for
 from flask import g as flaskg
-from werkzeug import ImmutableMultiDict
+from werkzeug import ImmutableMultiDict, FileStorage
 
 from MoinMoin.apps.frontend import views
 from MoinMoin import user
 from MoinMoin.util import crypto
 from MoinMoin._tests import wikiconfig
-import pytest
+
 
 class TestFrontend(object):
-    def test_root(self):
+    def _test_view(self, viewname, status='200 OK', data=('<html>', '</html>'), content_types=('text/html; charset=utf-8', ), viewopts=None, params=None):
+        if viewopts is None:
+            viewopts = {}
+        if params is None:
+            params = {}
+        print 'GET %s' % url_for(viewname, **viewopts)
         with self.app.test_client() as c:
-            rv = c.get('/') # / redirects to front page
-            assert rv.status == '302 FOUND'
+            rv = c.get(url_for(viewname, **viewopts), data=params)
+            assert rv.status == status
+            assert rv.headers['Content-Type'] in content_types
+            for item in data:
+                assert item in rv.data
+            return rv
+
+    def _test_view_post(self, viewname, status='302 FOUND', content_types=('text/html; charset=utf-8', ), data=('<html>', '</html>'), form=None, viewopts=None):
+        if viewopts is None:
+            viewopts = {}
+        if form is None:
+            form = {}
+        print 'POST %s' % url_for(viewname, **viewopts)
+        with self.app.test_client() as c:
+            rv = c.post(url_for(viewname, **viewopts), data=form)
+            assert rv.status == status
+            assert rv.headers['Content-Type'] in content_types
+            for item in data:
+                assert item in rv.data
+            return rv
+
+    def test_ajaxdelete_item_name_route(self):
+        self._test_view_post('frontend.ajaxdelete', status='200 OK', content_types=['application/json', ], data=['{', '}'], form=dict(
+            comment='Test',
+            itemnames='["DoesntExist"]',
+            ), viewopts=dict(item_name='DoesntExist'))
+
+    def test_ajaxdelete_no_item_name_route(self):
+        self._test_view_post('frontend.ajaxdelete', status='200 OK', content_types=['application/json', ], data=['{', '}'], form=dict(
+            comment='Test',
+            itemnames='["DoesntExist"]',
+            ))
+
+    def test_ajaxdestroy_item_name_route(self):
+        self._test_view_post('frontend.ajaxdestroy', status='200 OK', content_types=['application/json', ], data=['{', '}'], form=dict(
+            comment='Test',
+            itemnames='["DoesntExist"]',
+            ), viewopts=dict(item_name='DoesntExist'))
+
+    def test_ajaxdestroy_no_item_name_route(self):
+        self._test_view_post('frontend.ajaxdestroy', status='200 OK', content_types=['application/json', ], data=['{', '}'], form=dict(
+            comment='Test',
+            itemnames='["DoesntExist"]',
+            ))
+
+    def test_ajaxmodify(self):
+        self._test_view_post('frontend.ajaxmodify', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_jfu_server(self):
+        self._test_view_post('frontend.jfu_server', status='200 OK', data=['{', '}'], form=dict(
+            data_file=FileStorage(StringIO("Hello, world"), filename='C:\\fakepath\\DoesntExist.txt', content_type='text/plain'),
+            ), viewopts=dict(item_name='WillBeCreated'), content_types=['application/json', ])
+
+    def test_show_item(self):
+        self._test_view('frontend.show_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_show_dom(self):
+        self._test_view('frontend.show_dom', status='404 NOT FOUND', data=['<?xml', '>'], viewopts=dict(item_name='DoesntExist'), content_types=['text/xml; charset=utf-8', ])
+
+    def test_indexable(self):
+        self._test_view('frontend.indexable', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_highlight_item(self):
+        self._test_view('frontend.highlight_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_show_item_meta(self):
+        self._test_view('frontend.show_item_meta', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_content_item(self):
+        self._test_view('frontend.content_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_get_item(self):
+        self._test_view('frontend.get_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_download_item(self):
+        self._test_view('frontend.download_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_convert_item(self):
+        self._test_view('frontend.convert_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'), params=dict(contenttype='text/plain'))
+
+    def test_modify_item(self):
+        self._test_view('frontend.modify_item', status='200 OK', viewopts=dict(item_name='DoesntExist'))
+
+    def test_rename_item(self):
+        self._test_view('frontend.rename_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_delete_item(self):
+        self._test_view('frontend.delete_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_index(self):
+        self._test_view('frontend.index', status='200 OK', viewopts=dict(item_name='DoesntExist'))
+
+    def test_backrefs(self):
+        self._test_view('frontend.backrefs', status='200 OK', viewopts=dict(item_name='DoesntExist'))
+
+    def test_history(self):
+        self._test_view('frontend.history', status='200 OK', viewopts=dict(item_name='DoesntExist'))
+
+    def test_diff(self):
+        # TODO another test with valid rev1 and rev2 url args and an existing item is needed
+        self._test_view('frontend.diff', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_similar_names(self):
+        self._test_view('frontend.similar_names', viewopts=dict(item_name='DoesntExist'))
+
+    def test_sitemap(self):
+        self._test_view('frontend.sitemap', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist'))
+
+    def test_tagged_items(self):
+        self._test_view('frontend.tagged_items', status='200 OK', viewopts=dict(tag='DoesntExist'))
+
+    def test_root(self):
+        self._test_view('frontend.index')
 
     def test_robots(self):
-        with self.app.test_client() as c:
-            rv = c.get('/robots.txt')
-            assert rv.status == '200 OK'
-            assert rv.headers['Content-Type'] == 'text/plain; charset=utf-8'
-            assert 'Disallow:' in rv.data
+        self._test_view('frontend.robots', data=['Disallow:'], content_types=['text/plain; charset=utf-8', ])
+
+    def test_search(self):
+        self._test_view('frontend.search')
+
+    def test_revert_item(self):
+        self._test_view('frontend.revert_item', status='404 NOT FOUND', viewopts=dict(item_name='DoesntExist', rev='000000'))
+
+    def test_mychanges(self):
+        self._test_view('frontend.mychanges', viewopts=dict(userid='000000'))
+
+    def test_global_history(self):
+        self._test_view('frontend.global_history')
+
+    def test_wanted_items(self):
+        self._test_view('frontend.wanted_items')
+
+    def test_orphaned_items(self):
+        self._test_view('frontend.orphaned_items')
+
+    def test_quicklink_item(self):
+        self._test_view('frontend.quicklink_item', status='302 FOUND', viewopts=dict(item_name='DoesntExist'), data=['<!DOCTYPE HTML'])
+
+    def test_subscribe_item(self):
+        self._test_view('frontend.subscribe_item', status='302 FOUND', viewopts=dict(item_name='DoesntExist'), data=['<!DOCTYPE HTML'])
+
+    def test_register(self):
+        self._test_view('frontend.register')
+
+    def test_verifyemail(self):
+        self._test_view('frontend.verifyemail', status='302 FOUND', data=['<!DOCTYPE HTML'])
+
+    def test_lostpass(self):
+        self._test_view('frontend.lostpass')
+
+    def test_recoverpass(self):
+        self._test_view('frontend.recoverpass')
+
+    def test_login(self):
+        self._test_view('frontend.login')
+
+    def test_logout(self):
+        self._test_view('frontend.logout', status='302 FOUND', data=['<!DOCTYPE HTML'])
+
+    def test_usersettings(self):
+        self._test_view('frontend.usersettings')
+
+    def test_bookmark(self):
+        self._test_view('frontend.bookmark', status='302 FOUND', data=['<!DOCTYPE HTML'])
+
+    def test_diffraw(self):
+        # TODO another test with valid rev1 and rev2 url args and an existing item is needed
+        self._test_view('frontend.diffraw', status='404 NOT FOUND', data=[], viewopts=dict(item_name='DoesntExist'))
 
     def test_favicon(self):
-        with self.app.test_client() as c:
-            rv = c.get('/favicon.ico')
-            assert rv.status == '200 OK'
-            assert rv.headers['Content-Type'] in ['image/x-icon', 'image/vnd.microsoft.icon']
-            assert rv.data.startswith('\x00\x00') # "reserved word, should always be 0"
-
-    def test_404(self):
-        with self.app.test_client() as c:
-            rv = c.get('/DoesntExist')
-            assert rv.status == '404 NOT FOUND'
-            assert rv.headers['Content-Type'] == 'text/html; charset=utf-8'
-            assert '<html>' in rv.data
-            assert '</html>' in rv.data
+        rv = self._test_view('frontend.favicon', content_types=['image/x-icon', 'image/vnd.microsoft.icon', ], data=[])
+        assert rv.data.startswith('\x00\x00') # "reserved word, should always be 0"
 
-    def test_global_index(self):
-        with self.app.test_client() as c:
-            rv = c.get('/+index/')
-            assert rv.status == '200 OK'
-            assert rv.headers['Content-Type'] == 'text/html; charset=utf-8'
-            assert '<html>' in rv.data
-            assert '</html>' in rv.data
+    def test_global_tags(self):
+        self._test_view('frontend.global_tags')
 
-    def test_wanteds(self):
-        with self.app.test_client() as c:
-            rv = c.get('/+wanteds')
-            assert rv.status == '200 OK'
-            assert rv.headers['Content-Type'] == 'text/html; charset=utf-8'
-            assert '<html>' in rv.data
-            assert '</html>' in rv.data
-
-    def test_orphans(self):
-        with self.app.test_client() as c:
-            rv = c.get('/+orphans')
-            assert rv.status == '200 OK'
-            assert rv.headers['Content-Type'] == 'text/html; charset=utf-8'
-            assert '<html>' in rv.data
-            assert '</html>' in rv.data
 
 class TestUsersettings(object):
     def setup_method(self, method):
@@ -173,3 +311,4 @@
         if not self.user.exists():
             self.user = None
             pytest.skip("Can't create test user")
+
--- a/MoinMoin/apps/frontend/views.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/frontend/views.py	Sat Jan 28 17:42:01 2012 +0100
@@ -55,6 +55,7 @@
                             ALL_REVS, LATEST_REVS
 from MoinMoin.util import crypto
 from MoinMoin.util.interwiki import url_for_item
+from MoinMoin.search import SearchForm, ValidSearch
 from MoinMoin.security.textcha import TextCha, TextChaizedForm, TextChaValid
 from MoinMoin.storage.error import NoSuchItemError, NoSuchRevisionError
 from MoinMoin.signalling import item_displayed, item_modified
@@ -124,27 +125,6 @@
     return app.send_static_file('logos/favicon.ico')
 
 
-class ValidSearch(Validator):
-    """Validator for a valid search form
-    """
-    too_short_query_msg = L_('Search query too short.')
-
-    def validate(self, element, state):
-        if element['q'].value is None:
-            # no query, nothing to search for
-            return False
-        if len(element['q'].value) < 2:
-            return self.note_error(element, state, 'too_short_query_msg')
-        return True
-
-class SearchForm(Form):
-    q = String.using(optional=False, default=u'').with_properties(autofocus=True, placeholder=L_("Search Query"))
-    history = Boolean.using(label=L_('search also in non-current revisions'), optional=True)
-    submit = String.using(default=L_('Search'), optional=True)
-
-    validators = [ValidSearch()]
-
-
 @frontend.route('/+search', methods=['GET', 'POST'])
 def search():
     title_name = _("Search")
@@ -217,7 +197,6 @@
                               data_rendered=Markup(item._render_data()),
                               show_revision=show_revision,
                               show_navigation=show_navigation,
-                              search_form=SearchForm.from_defaults(),
                              )
     return Response(content, status)
 
@@ -249,8 +228,11 @@
 @frontend.route('/+indexable/<itemname:item_name>', defaults=dict(rev=CURRENT))
 def indexable(item_name, rev):
     from MoinMoin.storage.middleware.indexing import convert_to_indexable
-    item = flaskg.storage[item_name]
-    rev = item[rev]
+    try:
+        item = flaskg.storage[item_name]
+        rev = item[rev]
+    except KeyError:
+        abort(404)
     content = convert_to_indexable(rev.meta, rev.data, item_name)
     return Response(content, 200, mimetype='text/plain')
 
@@ -490,7 +472,10 @@
         TextCha(form).amend_form()
         if form.validate():
             comment = form['comment'].value
-            item.delete(comment)
+            try:
+                item.delete(comment)
+            except AccessDenied:
+                abort(403)
             return redirect(url_for_item(item_name))
     return render_template(item.delete_template,
                            item=item, item_name=item_name,
@@ -586,7 +571,10 @@
         TextCha(form).amend_form()
         if form.validate():
             comment = form['comment'].value
-            item.destroy(comment=comment, destroy_item=destroy_item)
+            try:
+                item.destroy(comment=comment, destroy_item=destroy_item)
+            except AccessDenied:
+                abort(403)
             return redirect(url_for_item(item_name))
     return render_template(item.destroy_template,
                            item=item, item_name=item_name,
@@ -713,7 +701,7 @@
     refs_here = _backrefs(item_name)
     return render_template('item_link_list.html',
                            item_name=item_name,
-                           headline=_(u'Refers Here'),
+                           headline=_(u"Items which refer to '%(item_name)s'", item_name=item_name),
                            item_names=refs_here
                           )
 
@@ -958,64 +946,56 @@
 @frontend.route('/+register', methods=['GET', 'POST'])
 def register():
     title_name = _(u'Register')
-    # is openid_submit in the form?
     isOpenID = 'openid_submit' in request.values
 
     if isOpenID:
         # this is an openid continuation
         if not _using_openid_auth():
             return Response('No OpenIDAuth in auth list', 403)
-
         template = 'openid_register.html'
-        if request.method == 'GET':
-            form = OpenIDForm.from_defaults()
-            # we got an openid from the multistage redirect
-            oid = request.values.get('openid_openid')
-            if oid:
-                form['openid'] = oid
-            TextCha(form).amend_form()
-
-        elif request.method == 'POST':
-            form = OpenIDForm.from_flat(request.form)
-            TextCha(form).amend_form()
-
-            if form.validate():
-                msg = user.create_user(username=form['username'].value,
-                                       password=form['password1'].value,
-                                       email=form['email'].value,
-                                       openid=form['openid'].value,
-                                      )
-                if msg:
-                    flash(msg, "error")
-                else:
-                    flash(_('Account created, please log in now.'), "info")
-                    return redirect(url_for('.show_root'))
-
+        FormClass = OpenIDForm
     else:
         # not openid registration and no MoinAuth
         if not _using_moin_auth():
             return Response('No MoinAuth in auth list', 403)
-
         template = 'register.html'
-        if request.method == 'GET':
-            form = RegistrationForm.from_defaults()
-            TextCha(form).amend_form()
+        FormClass = RegistrationForm
 
-        elif request.method == 'POST':
-            form = RegistrationForm.from_flat(request.form)
-            TextCha(form).amend_form()
+    if request.method == 'GET':
+        form = FormClass.from_defaults()
+        if isOpenID:
+            oid = request.values.get('openid_openid')
+            if oid:
+                form['openid'] = oid
+        TextCha(form).amend_form()
+    elif request.method == 'POST':
+        form = FormClass.from_flat(request.form)
+        TextCha(form).amend_form()
 
-            if form.validate():
-                msg = user.create_user(username=form['username'].value,
-                                       password=form['password1'].value,
-                                       email=form['email'].value,
-                                       openid=form['openid'].value,
-                                      )
-                if msg:
-                    flash(msg, "error")
+        if form.validate():
+            user_kwargs = {
+                'username': form['username'].value,
+                'password': form['password1'].value,
+                'email': form['email'].value,
+                #'openid': form['openid'].value,
+            }
+            if app.cfg.user_email_verification:
+                user_kwargs['is_disabled'] = True
+            msg = user.create_user(**user_kwargs)
+            if msg:
+                flash(msg, "error")
+            else:
+                if app.cfg.user_email_verification:
+                    u = user.User(auth_username=user_kwargs['username'])
+                    is_ok, msg = u.mailVerificationLink()
+                    if is_ok:
+                        flash(_('Account verification required, please see the email we sent to your address.'), "info")
+                    else:
+                        flash(_('An error occurred while sending the verification email: "%(message)s" Please contact an administrator to activate your account.',
+                            message=msg), "error")
                 else:
                     flash(_('Account created, please log in now.'), "info")
-                    return redirect(url_for('.show_root'))
+                return redirect(url_for('.show_root'))
 
     return render_template(template,
                            title_name=title_name,
@@ -1023,6 +1003,21 @@
                           )
 
 
+@frontend.route('/+verifyemail', methods=['GET'])
+def verifyemail():
+    u = None
+    if 'username' in request.values and 'token' in request.values:
+        u = user.User(auth_username=request.values['username'])
+        token = request.values['token']
+    if u and u.disabled and u.validate_recovery_token(token):
+        u.disabled = False
+        u.save()
+        flash(_("Your account has been activated, you can log in now."), "info")
+    else:
+        flash(_('Your token is invalid!'), "error")
+    return redirect(url_for('.show_root'))
+
+
 class ValidLostPassword(Validator):
     """Validator for a valid lost password form
     """
@@ -1208,12 +1203,16 @@
                           )
 
 
+def _logout():
+    for key in ['user.itemid', 'user.auth_method', 'user.auth_attribs', ]:
+        if key in session:
+            del session[key]
+
+
 @frontend.route('/+logout')
 def logout():
     flash(_("You are now logged out."), "info")
-    for key in ['user.itemid', 'user.auth_method', 'user.auth_attribs', ]:
-        if key in session:
-            del session[key]
+    _logout()
     return redirect(url_for('.show_root'))
 
 
@@ -1277,9 +1276,8 @@
     submit = String.using(default=L_('Save'), optional=True)
 
 
-@frontend.route('/+usersettings', defaults=dict(part='main'), methods=['GET'])
-@frontend.route('/+usersettings/<part>', methods=['GET', 'POST'])
-def usersettings(part):
+@frontend.route('/+usersettings', methods=['GET', 'POST'])
+def usersettings():
     # TODO use ?next=next_location check if target is in the wiki and not outside domain
     title_name = _('User Settings')
 
@@ -1310,7 +1308,7 @@
         results_per_page = Integer.using(label=L_('History results per page')).with_properties(placeholder=L_("Number of results per page (0=no paging)")).validated_by(ValueAtLeast(0))
         submit = String.using(default=L_('Save'), optional=True)
 
-    dispatch = dict(
+    form_classes = dict(
         personal=UserSettingsPersonalForm,
         password=UserSettingsPasswordForm,
         notification=UserSettingsNotificationForm,
@@ -1318,53 +1316,108 @@
         navigation=UserSettingsNavigationForm,
         options=UserSettingsOptionsForm,
     )
-    FormClass = dispatch.get(part)
-    if FormClass is None:
-        # 'main' part or some invalid part
-        return render_template('usersettings.html',
-                               part='main',
-                               title_name=title_name,
-                              )
-    if request.method == 'GET':
-        form = FormClass.from_object(flaskg.user)
-        form['submit'].set_default() # XXX from_object() kills all values
-    elif request.method == 'POST':
-        form = FormClass.from_flat(request.form)
-        if form.validate():
-            # successfully modified everything
-            success = True
-            if part == 'password':
-                flaskg.user.enc_password = crypto.crypt_password(form['password1'].value)
-                flaskg.user.save()
-                flash(_("Your password has been changed."), "info")
+    forms = dict()
+
+    if request.method == 'POST':
+        part = request.form.get('part')
+        if part not in form_classes:
+            # the current part does not exist
+            if request.is_xhr:
+                # if the request is made via XHR, we return 404 Not Found
+                abort(404)
+            # otherwise we basically fall back to a normal GET request
+            part = None
+
+        if part:
+            # create form object from request.form
+            form = form_classes[part].from_flat(request.form)
+
+            # save response to a dict as we can't use HTTP redirects or flash() for XHR requests
+            response = dict(
+                form = None,
+                flash = [],
+                redirect = None,
+            )
+
+            if form.validate():
+                # successfully modified everything
+                success = True
+                if part == 'password':
+                    flaskg.user.enc_password = crypto.crypt_password(form['password1'].value)
+                    flaskg.user.save()
+                    response['flash'].append((_("Your password has been changed."), "info"))
+                else:
+                    if part == 'personal':
+                        if form['openid'].value != flaskg.user.openid and user.search_users(openid=form['openid'].value):
+                            # duplicate openid
+                            response['flash'].append((_("This openid is already in use."), "error"))
+                            success = False
+                        if form['name'].value != flaskg.user.name and user.search_users(name_exact=form['name'].value):
+                            # duplicate name
+                            response['flash'].append((_("This username is already in use."), "error"))
+                            success = False
+                    if part == 'notification':
+                        if (form['email'].value != flaskg.user.email and
+                            user.search_users(email=form['email'].value) and app.cfg.user_email_unique):
+                            # duplicate email
+                            response['flash'].append((_('This email is already in use'), 'error'))
+                            success = False
+                    if success:
+                        user_old_email = flaskg.user.email
+                        form.update_object(flaskg.user, omit=['submit']) # don't save submit button value :)
+                        if part == 'notification' and app.cfg.user_email_verification and form['email'].value != user_old_email:
+                            # disable account
+                            flaskg.user.disabled = True
+                            # send verification mail
+                            is_ok, msg = flaskg.user.mailVerificationLink()
+                            if is_ok:
+                                _logout()
+                                flaskg.user.save()
+                                response['flash'].append((_('Your account has been disabled because you changed your email address. Please see the email we sent to your address to reactivate it.'), "info"))
+                                response['redirect'] = url_for('.show_root')
+                            else:
+                                # sending the verification email didn't work. reset email change and alert the user.
+                                flaskg.user.disabled = False
+                                flaskg.user.email = user_old_email
+                                flaskg.user.save()
+                                response['flash'].append((_('Your email address was not changed because sending the verification email failed. Please try again later.'), "error"))
+                        else:
+                            flaskg.user.save()
+
+            if not response['flash']:
+                # if no flash message was added until here, we add a generic success message
+                response['flash'].append((_("Your changes have been saved."), "info"))
+
+            if response['redirect'] is not None or not request.is_xhr:
+                # if we redirect or it is no XHR request, we just flash() the messages normally
+                for f in response['flash']:
+                    flash(*f)
+
+            if request.is_xhr:
+                # if it is a XHR request, render the part from the usersettings_ajax.html template
+                # and send the response encoded as an JSON object
+                response['form'] = render_template('usersettings_ajax.html',
+                                                   part=part,
+                                                   form=form,
+                                                  )
+                return jsonify(**response)
             else:
-                if part == 'personal':
-                    if form['openid'].value != flaskg.user.openid and user.search_users(openid=form['openid'].value):
-                        # duplicate openid
-                        flash(_("This openid is already in use."), "error")
-                        success = False
-                    if form['name'].value != flaskg.user.name and user.search_users(name_exact=form['name'].value):
-                        # duplicate name
-                        flash(_("This username is already in use."), "error")
-                        success = False
-                if part == 'notification':
-                    if (form['email'].value != flaskg.user.email and
-                        user.search_users(email=form['email'].value) and app.cfg.user_email_unique):
-                        # duplicate email
-                        flash(_('This email is already in use'), 'error')
-                        success = False
-                if success:
-                    form.update_object(flaskg.user, omit=['submit']) # don't save submit button value :)
-                    flaskg.user.save()
-                    return redirect(url_for('.usersettings'))
-                else:
-                    # reset to valid values
-                    form = FormClass.from_object(flaskg.user)
-                    form['submit'].set_default() # XXX from_object() kills all values
+                # if it is not a XHR request but there is an redirect pending, we use a normal HTTP redirect
+                if response['redirect'] is not None:
+                    return redirect(response['redirect'])
+
+            # if the view did not return until here, we add the current form to the forms dict
+            # and continue with rendering the normal template
+            forms[part] = form
+
+    # initialize all remaining forms
+    for p, FormClass in form_classes.iteritems():
+        if p not in forms:
+            forms[p] = FormClass.from_object(flaskg.user)
+
     return render_template('usersettings.html',
                            title_name=title_name,
-                           part=part,
-                           form=form,
+                           form_objs=forms,
                           )
 
 
@@ -1393,6 +1446,21 @@
     return redirect(url_for('.global_history'))
 
 
+def get_revs():
+    """
+    get 2 revids from values
+    """
+    rev1 = request.values.get('rev1')
+    rev2 = request.values.get('rev2')
+    if rev1 is None:
+        # we require at least rev1
+        abort(404)
+    if rev2 is None:
+        # rev2 is optional, use current rev if not given
+        rev2 = CURRENT
+    return rev1, rev2
+
+
 @frontend.route('/+diffraw/<path:item_name>')
 def diffraw(item_name):
     # TODO get_item and get_revision calls may raise an AccessDenied.
@@ -1400,11 +1468,10 @@
     #      If it happens for get_revision, we may just want to skip that rev in the list
     # TODO verify if it does crash when the item does not exist
     try:
-        item = flaskg.storage.get_item(item_name)
+        item = flaskg.storage[item_name]
     except AccessDenied:
         abort(403)
-    rev1 = request.values.get('rev1')
-    rev2 = request.values.get('rev2')
+    rev1, rev2 = get_revs()
     return _diff_raw(item, rev1, rev2)
 
 
@@ -1425,8 +1492,7 @@
         rev2 = CURRENT  # and compare it with latest we have
     else:
         # otherwise we should get the 2 revids directly
-        rev1 = request.values.get('rev1')
-        rev2 = request.values.get('rev2')
+        rev1, rev2 = get_revs()
     return _diff(item, rev1, rev2)
 
 
@@ -1447,8 +1513,11 @@
 
 
 def _diff(item, revid1, revid2):
-    oldrev = item[revid1]
-    newrev = item[revid2]
+    try:
+        oldrev = item[revid1]
+        newrev = item[revid2]
+    except KeyError:
+        abort(404)
     commonmt = _common_type(oldrev.meta[CONTENTTYPE], newrev.meta[CONTENTTYPE])
 
     try:
@@ -1458,6 +1527,7 @@
     rev_ids = [CURRENT]  # XXX TODO we need a reverse sorted list
     return render_template(item.diff_template,
                            item=item, item_name=item.name,
+                           diff_html=Markup(item._render_data_diff(oldrev, newrev)),
                            rev=item.rev,
                            first_rev_id=rev_ids[0],
                            last_rev_id=rev_ids[-1],
@@ -1469,7 +1539,7 @@
 def _diff_raw(item, revid1, revid2):
     oldrev = item[revid1]
     newrev = item[revid2]
-    commonmt = _common_type(oldrev, newrev)
+    commonmt = _common_type(oldrev.meta[CONTENTTYPE], newrev.meta[CONTENTTYPE])
 
     try:
         item = Item.create(item.name, contenttype=commonmt, rev_id=newrev.revid)
@@ -1498,7 +1568,7 @@
             if rank == wanted_rank:
                 item_names.append(name)
     return render_template("item_link_list.html",
-                           headline=_("Items with similar names"),
+                           headline=_("Items with similar names to '%(item_name)s'", item_name=item_name),
                            item_name=item_name, # XXX no item
                            item_names=item_names)
 
@@ -1625,6 +1695,9 @@
     """
     sitemap view shows item link structure, relative to current item
     """
+    # first check if this item exists
+    if not flaskg.storage[item_name]:
+        abort(404)
     sitemap = NestedItemListBuilder().recurse_build([item_name])
     del sitemap[0] # don't show current item name as sole toplevel list item
     return render_template('sitemap.html',
@@ -1656,9 +1729,9 @@
         # does not recurse
         try:
             item = flaskg.storage[name]
-        except AccessDenied:
+            rev = item[CURRENT]
+        except (AccessDenied, KeyError):
             return []
-        rev = item[CURRENT]
         itemlinks = rev.meta.get(ITEMLINKS, [])
         return [child for child in itemlinks if self.is_ok(child)]
 
--- a/MoinMoin/apps/misc/_tests/test_misc.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/apps/misc/_tests/test_misc.py	Sat Jan 28 17:42:01 2012 +0100
@@ -5,9 +5,10 @@
 MoinMoin - basic tests for misc views
 """
 
+from flask import url_for
+
 from MoinMoin._tests import wikiconfig
 
-
 class TestMisc(object):
     class Config(wikiconfig.Config):
         """
@@ -16,7 +17,7 @@
 
     def test_global_sitemap(self):
         with self.app.test_client() as c:
-            rv = c.get('/+misc/sitemap')
+            rv = c.get(url_for('misc.sitemap'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'text/xml; charset=utf-8'
             assert rv.data.startswith('<?xml')
@@ -25,6 +26,6 @@
 
     def test_urls_names(self):
         with self.app.test_client() as c:
-            rv = c.get('/+misc/urls_names')
+            rv = c.get(url_for('misc.urls_names'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'text/plain; charset=utf-8'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/apps/serve/_tests/test_serve.py	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,23 @@
+# Copyright: 2011 Sam Toyer
+# License: GNU GPL V2 (or any later version), see LICENSE.txt for details
+
+"""
+    MoinMoin - tests for "serve" app
+"""
+
+from flask import url_for
+
+class TestServe(object):
+    def test_index(self):
+        with self.app.test_client() as c:
+            rv = c.get(url_for('serve.index'))
+            assert rv.status == '200 OK'
+            assert rv.headers['Content-Type'] == 'text/plain'
+
+    def test_files(self):
+        with self.app.test_client() as c:
+            rv = c.get(url_for('serve.files', name="DoesntExist"))
+            assert rv.status == '404 NOT FOUND'
+            assert rv.headers['Content-Type'] == 'text/html'
+            assert '<!DOCTYPE HTML' in rv.data
+
--- a/MoinMoin/config/default.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/config/default.py	Sat Jan 28 17:42:01 2012 +0100
@@ -374,8 +374,8 @@
         ('special.comments', L_('Comments'), L_('Switch showing comments on or off'), True, ),
         ('frontend.highlight_item', L_('Highlight'), L_('Show with Syntax-Highlighting'), True, ),
         ('frontend.show_item_meta', L_('Meta'), L_('Display Metadata'), True, ),
-        ('frontend.quicklink_item', None, L_('Create or remove a navigation link to this item'), True, ),
-        ('frontend.subscribe_item', None, L_('Switch notifications about item changes on or off'), True, ),
+        ('frontend.quicklink_item', None, L_('Create or remove a navigation link to this item'), False, ),
+        ('frontend.subscribe_item', None, L_('Switch notifications about item changes on or off'), False, ),
         ('frontend.rename_item', L_('Rename'), L_('Rename this item'), True, ),
         ('frontend.delete_item', L_('Delete'), L_('Delete this item'), True, ),
         ('frontend.destroy_item', L_('Destroy'), L_('Completely destroy this item'), True, ),
@@ -552,6 +552,8 @@
     'user': ('Users / User settings', None, (
       ('email_unique', True,
        "if True, check email addresses for uniqueness and don't accept duplicates."),
+      ('email_verification', False,
+       "if True, require a new user to verify his or her email address before the first login."),
 
       ('homewiki', u'Self',
        "interwiki name of the wiki where the user home pages are located [Unicode] - useful if you have ''many'' users. You could even link to nonwiki \"user pages\" if the wiki username is in the target URL."),
--- a/MoinMoin/constants/misc.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/constants/misc.py	Sat Jan 28 17:42:01 2012 +0100
@@ -37,7 +37,7 @@
 del c
 
 # Other stuff
-url_schemas = ['http', 'https', 'ftp', 'file',
+uri_schemes = ['http', 'https', 'ftp', 'file',
                'mailto', 'nntp', 'news',
                'ssh', 'telnet', 'irc', 'ircs', 'xmpp', 'mumble',
                'webcal', 'ed2k', 'apt', 'rootz',
--- a/MoinMoin/converter/_table.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_table.py	Sat Jan 28 17:42:01 2012 +0100
@@ -14,10 +14,21 @@
     """
     Mixin to support building a DOM table.
     """
-    def build_dom_table(self, rows):
+    def build_dom_table(self, rows, head=None, cls=None):
         """
         Build a DOM table with data from <rows>.
         """
+        table = moin_page.table()
+        if cls is not None:
+            table.attrib[moin_page('class')] = cls
+        if head is not None:
+            table_head = moin_page.table_header()
+            table_row = moin_page.table_row()
+            for cell in head:
+                table_cell = moin_page.table_cell(children=[cell, ])
+                table_row.append(table_cell)
+            table_head.append(table_row)
+            table.append(table_head)
         table_body = moin_page.table_body()
         for row in rows:
             table_row = moin_page.table_row()
@@ -25,5 +36,6 @@
                 table_cell = moin_page.table_cell(children=[cell, ])
                 table_row.append(table_cell)
             table_body.append(table_row)
-        return moin_page.table(children=[table_body, ])
+        table.append(table_body)
+        return table
 
--- a/MoinMoin/converter/_tests/test_creole_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_tests/test_creole_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -42,6 +42,8 @@
                 '<page><body><p><a xlink:href="http://moinmo.in/">http://moinmo.in/</a></p></body></page>'),
             (u'[[http://moinmo.in/]]',
                 '<page><body><p><a xlink:href="http://moinmo.in/">http://moinmo.in/</a></p></body></page>'),
+            (u'[[javascript:alert("xss")]]',
+                '<page><body><p><a xlink:href="wiki.local:javascript:alert%28%22xss%22%29">javascript:alert("xss")</a></p></body></page>'),
             (u'[[http://moinmo.in/|MoinMoin]]',
                 '<page><body><p><a xlink:href="http://moinmo.in/">MoinMoin</a></p></body></page>'),
             (u'[[MoinMoin]]',
--- a/MoinMoin/converter/_tests/test_docbook_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_tests/test_docbook_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -272,6 +272,10 @@
             ('<article><para><olink targetdoc="uri" targetptr="anchor">link</olink></para></article>',
             # <page><body><div html:class="article"><para><a xlink:href="uri#anchor">link</a></para></div></body></page>
              '/page/body/div/p/a[@xlink:href="uri#anchor"][text()="link"]'),
+            # Link w/ javascript: scheme
+            ('<article><para><ulink url="javascript:alert(\'xss\')">link</ulink></para></article>',
+            # <page><body><div html:class="article"><p><a xlink:href="url:test">link</a></p></div></body></page>
+             '/page/body/div/p/a[@xlink:href=""][text()="link"]'),
         ]
         for i in data:
             yield (self.do, ) + i
--- a/MoinMoin/converter/_tests/test_html_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_tests/test_html_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -175,6 +175,9 @@
             ('<html><base href="http://www.base-url.com/" /><body><div><p><a href="myPage.html">Test</a></p></div></body></html>',
               # <page><body><div><p><a xlink:href="http://www.base-url.com/myPage.html">Test</a></p></div></body></page>
               '/page/body/div/p/a[@xlink:href="http://www.base-url.com/myPage.html"]'),
+            ('<html><p><a href="javascript:alert(\'hi\')">Test</a></p></html>',
+              # <page><body><p>Test</p></body></page>
+                '/page/body/p/[text()="Test"]'),
         ]
         for i in data:
             yield (self.do, ) + i
--- a/MoinMoin/converter/_tests/test_mediawiki_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_tests/test_mediawiki_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -45,7 +45,8 @@
 ====level 4====
 =====level 5=====
 ======level 6======
-""", u'<page><body><h outline-level="1">level 1</h><h outline-level="2">level 2</h><h outline-level="3">level 3</h><h outline-level="4">level 4</h><h outline-level="5">level 5</h><h outline-level="6">level 6</h></body></page>')
+""", u'<page><body><h outline-level="1">level 1</h><h outline-level="2">level 2</h><h outline-level="3">level 3</h><h outline-level="4">level 4</h><h outline-level="5">level 5</h><h outline-level="6">level 6</h></body></page>'),
+            (u"[javascript:alert('xss')]", "<page><body><p>[javascript:alert('xss')]</p></body></page>"),
         ]
         for i in data:
             yield (self.do, ) + i
@@ -138,7 +139,17 @@
             (u"[http://external.link]", u'<page><body><p><a xlink:href="http://external.link"></a></p></body></page>'),
             (u"[http://external.link alt text]", u'<page><body><p><a xlink:href="http://external.link">alt text</a></p></body></page>'),
             (u"[[SomeLink|Some text]]", u'<page><body><p><a xlink:href="wiki.local:SomeLink">Some text</a></p></body></page>'),
-            (u"[[File:Test.jpg|test]]", u'<page><body><p><object alt="test" xlink:href="wiki.local:Test.jpg?do=get">test</object></p></body></page>')
+            (u"[[SomeLink|arg1=value|arg2=otherval|Some text]]", u'<page><body><p><a xlink:href="wiki.local:SomeLink?arg1=value&amp;arg2=otherval">Some text</a></p></body></page>'),
+            (u"[[File:Test.jpg|test]]", u'<page><body><p><object alt="test" xlink:href="wiki.local:Test.jpg?do=get">test</object></p></body></page>'),
+            (u"[[File:MyImage.png]]", u'<page><body><p><object alt="MyImage.png" xlink:href="wiki.local:MyImage.png?do=get">MyImage.png</object></p></body></page>'),
+            (u"[[File:MyImage.png|arg=http://google.com|caption]]", u'<page><body><p><object alt="caption" xlink:href="wiki.local:MyImage.png?do=get&amp;arg=http%253A%252F%252Fgoogle.com">caption</object></p></body></page>'),
+            (u"[[File:Test.png|do=get|arg1=test|arg2=something else]]", u'<page><body><p><object alt="Test.png" xlink:href="wiki.local:Test.png?do=get&amp;arg2=something+else&amp;arg1=test">Test.png</object></p></body></page>'),
+            # The do=xxx part is just to test if do in args is being updated correctly, it's invalid otherwise
+            (u"[[File:Test2.png|do=xxx|caption|arg1=test]]", u'<page><body><p><object alt="caption" xlink:href="wiki.local:Test2.png?do=xxx&amp;arg1=test">caption</object></p></body></page>'),
+            (u"[[File:myimg.png|'Graph showing width |= k for 5 < k < 10']]", u'<page><body><p><object alt="Graph showing width |= k for 5 &lt; k &lt; 10" xlink:href="wiki.local:myimg.png?do=get">Graph showing width |= k for 5 &lt; k &lt; 10</object></p></body></page>'),
+            (u"[[File:myimg.png|arg1='longish caption value with |= to test'|arg2=other|test stuff]]", u'<page><body><p><object alt="test stuff" xlink:href="wiki.local:myimg.png?arg1=longish+caption+value+with+%257C%253D+to+test&amp;arg2=other&amp;do=get">test stuff</object></p></body></page>'),
+            # Unicode test
+            (u"[[File:Test.jpg|\xe8]]", u'<page><body><p><object alt="\xe8" xlink:href="wiki.local:Test.jpg?do=get">\xe8</object></p></body></page>')
         ]
         for i in data:
             yield (self.do, ) + i
--- a/MoinMoin/converter/_tests/test_moinwiki_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_tests/test_moinwiki_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -36,6 +36,8 @@
                 '<page><body><p>Text</p><p>Test</p></body></page>'),
             (u'[[http://moinmo.in/]]',
                 '<page><body><p><a xlink:href="http://moinmo.in/">http://moinmo.in/</a></p></body></page>'),
+            (u'[[javascript:alert("xss")]]',
+                '<page><body><p><a xlink:href="wiki.local:javascript:alert%28%22xss%22%29">javascript:alert("xss")</a></p></body></page>'),
             (u'[[http://moinmo.in/|MoinMoin]]',
                 '<page><body><p><a xlink:href="http://moinmo.in/">MoinMoin</a></p></body></page>'),
             (u'[[MoinMoin]]',
--- a/MoinMoin/converter/_tests/test_rst_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_tests/test_rst_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -37,6 +37,7 @@
             (u'**Text**', '<page><body><p><strong>Text</strong></p></body></page>' ),
             (u'*Text*', '<page><body><p><emphasis>Text</emphasis></p></body></page>' ),
             (u'``Text``', '<page><body><p><code>Text</code></p></body></page>' ),
+            (u"`Text <javascript:alert('xss')>`_", u'<page><body><p>Text</p></body></page>'),
         ]
         for i in data:
             yield (self.do, ) + i
--- a/MoinMoin/converter/_util.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/_util.py	Sat Jan 28 17:42:01 2012 +0100
@@ -8,8 +8,13 @@
 
 from __future__ import absolute_import, division
 
+from MoinMoin.config import uri_schemes
+from MoinMoin.util.iri import Iri
 from MoinMoin.util.mime import Type
 
+def allowed_uri_scheme(uri):
+    parsed = Iri(uri)
+    return not parsed.scheme or parsed.scheme in uri_schemes
 
 def decode_data(data, contenttype=None):
     """
--- a/MoinMoin/converter/archive_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/archive_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -17,6 +17,7 @@
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
+from MoinMoin.i18n import _, L_, N_
 from MoinMoin.util.iri import Iri
 from MoinMoin.util.tree import moin_page, xlink
 
@@ -47,16 +48,15 @@
     def process_size(self, size):
         return unicode(size)
 
-    def __call__(self, fileobj, contenttype=None, arguments=None):
-        # we get a revision as fileobj
-        self.item_name = fileobj.item.name
+    def __call__(self, rev, contenttype=None, arguments=None):
+        self.item_name = rev.item.name
         try:
-            contents = self.list_contents(fileobj)
+            contents = self.list_contents(rev.data)
             contents = [(self.process_size(size),
                          self.process_datetime(dt),
                          self.process_name(name),
                         ) for size, dt, name in contents]
-            return self.build_dom_table(contents)
+            return self.build_dom_table(contents, head=[_("Size"), _("Date"), _("Name")], cls='zebra')
         except ArchiveException as err:
             logging.exception("An exception within archive file handling occurred:")
             # XXX we also use a table for error reporting, could be
--- a/MoinMoin/converter/creole_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/creole_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -27,7 +27,7 @@
 
 import re
 
-from MoinMoin import wikiutil
+from MoinMoin import config, wikiutil
 from MoinMoin.util.iri import Iri
 from MoinMoin.util.tree import moin_page, xlink, xinclude
 
@@ -495,13 +495,13 @@
             (^ | (?<=\s | [.,:;!?()/=]))
             (?P<escaped_url>~)?
             (?P<url_target>
-                # TODO: config.url_schemas
-                (http|https|ftp|nntp|news|mailto|telnet|file|irc):
+                (%(uri_schemes)s)
+                :
                 \S+?
             )
             ($ | (?=\s | [,.:;!?()] (\s | $)))
         )
-    """
+    """ % dict(uri_schemes='|'.join(config.uri_schemes))
 
     def inline_url_repl(self, stack, url, url_target, escaped_url=None):
         """Handle raw urls in text."""
--- a/MoinMoin/converter/docbook_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/docbook_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -27,7 +27,7 @@
 from MoinMoin.util.tree import moin_page, xlink, docbook, xml, html
 
 from ._wiki_macro import ConverterMacro
-from ._util import decode_data, normalize_split_text
+from ._util import allowed_uri_scheme, decode_data, normalize_split_text
 
 
 class NameSpaceError(Exception):
@@ -641,7 +641,7 @@
         """
         attrib = {}
         for key, value in element.attrib.iteritems():
-            if key.uri == xlink:
+            if key.uri == xlink and allowed_uri_scheme(value):
                 attrib[key] = value
         linkend = element.get('linkend')
         if linkend:
@@ -669,7 +669,7 @@
         """
         targetdoc = element.get('targetdoc')
         targetptr = element.get('targetptr')
-        if targetdoc and targetptr:
+        if targetdoc and targetptr and allowed_uri_scheme(targetdoc):
             attrib = {}
             attrib[xlink.href] = ''.join([targetdoc, '#', targetptr])
             return self.new_copy(moin_page.a, element,
@@ -984,7 +984,7 @@
         # The namespace does not always work, so we will try to retrive the attribute whatever
         if not(href):
             for key, value in element.attrib.iteritems():
-                if key.name == 'url':
+                if key.name == 'url' and allowed_uri_scheme(value):
                     href = value
         key = xlink.href
         attrib[key] = href
--- a/MoinMoin/converter/html_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/html_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -24,7 +24,7 @@
 from MoinMoin.util.tree import html, moin_page, xlink, xml
 
 from ._wiki_macro import ConverterMacro
-from ._util import decode_data, normalize_split_text
+from ._util import allowed_uri_scheme, decode_data, normalize_split_text
 
 
 class Converter(object):
@@ -356,10 +356,14 @@
         """
         key = xlink('href')
         attrib = {}
+        href = element.get(html.href)
         if self.base_url:
-            attrib[key] = ''.join([self.base_url, element.get(html.href)])
+            attrib[key] = ''.join([self.base_url, href])
         else:
-            attrib[key] = element.get(html.href)
+            if allowed_uri_scheme(href):
+                attrib[key] = href
+            else:
+                return href
         return self.new_copy(moin_page.a, element, attrib)
 
     def visit_xhtml_img(self, element):
--- a/MoinMoin/converter/html_out.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/html_out.py	Sat Jan 28 17:42:01 2012 +0100
@@ -521,6 +521,7 @@
         elem.append(html.a(attrib={
             html.href: "#{0}".format(id),
             html.class_: "permalink",
+            html.title_: _("Link to this heading")
         }, children=(u"¶", )))
         self._headings.append((elem, level, id))
 
--- a/MoinMoin/converter/mediawiki_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/mediawiki_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -584,7 +584,7 @@
             \[
             \s*
             (?P<external_link_url>
-                    [a-zA-Z0-9+.-]+
+                    (%(uri_schemes)s)
                     :
                     [^ ]*
             )
@@ -593,21 +593,54 @@
             \s*
             \]
         )
-    """
+    """ % dict(uri_schemes='|'.join(config.uri_schemes))
+
+    def parse_args(self, input):
+        """
+        Parses media wiki arguments, this is taken from _args_wiki > parse function. The primary difference
+        being that mediawiki breaks on pipes whereas the default parser breaks on spaces. Apart from that
+        this parser also supports a few extra characters such as "<, >, ., /", mostly for URL linking
+
+        :param input: can be like a|b|c=f|something else caption|g='long caption'|link=http://google.com
+        :return Arguments instance
+        """
+        parse_rules = r'''
+        (?:
+            (?P<key>[\w-]+)=    # Matches 'key=' part of the string, optional
+        )?
+        (?:
+            (?P<unquote_val>[-\w\s:\./<>]+) # Unquoted value, intended to break after a |
+            |
+            # Matches quoted values with every character, breaks after the quote
+            "(?P<dquote_val>.*?)(?<!\\)"    # Quoted value with double quotes
+            |
+            '(?P<squote_val>.*?)(?<!\\)'    # Quoted value with single quotes
+        )
+        '''
+        parse_re = re.compile(parse_rules, re.X | re.U)
+        ret = Arguments()
+        for match in parse_re.finditer(input):
+            key = match.group('key')
+            value = match.group('unquote_val') or match.group('squote_val') or match.group('dquote_val')
+            if key:
+                ret.keyword[key] = value
+            else:
+                ret.positional.append(value)
+        return ret
 
     def inline_link_repl(self, stack, link, link_url=None, link_item=None,
-                            link_args=None, external_link_url=None, alt_text=''):
+                            link_args=u'', external_link_url=None, alt_text=u''):
         """Handle all kinds of links."""
         link_text = ''
-        if link_args and len(link_args.split('|')) > 2:
-            link_args = parse_arguments(' '.join(link_args.split('|')[:-1])) # TODO needs parsing for mediawiki_args
-            query = url_encode(link_args.keyword, charset=config.charset, encode_keys=True)
-        else:
-            if link_args:
-                link_text = link_args.split('|')[-1]
-                link_args = parse_arguments(' '.join(link_args.split('|')[:-1]))
-
-            query = None
+        link_args_list = []
+        # Remove the first pipe/space, example of link_args : |arg1|arg2 or " arg1 arg2"
+        parsed_args = self.parse_args(link_args[1:])
+        query = None
+        if parsed_args.keyword:
+            query = url_encode(parsed_args.keyword, charset=config.charset, encode_keys=True)
+        # Take the last of positional parameters as link_text(caption)
+        if parsed_args.positional:
+            link_text = parsed_args.positional.pop()
         if link_item is not None:
             if '#' in link_item:
                 path, fragment = link_item.rsplit('#', 1)
@@ -618,7 +651,7 @@
         else:
             if link_url and len(link_url.split(':')) > 0 and link_url.split(':')[0] == 'File':
                 object_item = ':'.join(link_url.split(':')[1:])
-                args = link_args.keyword
+                args = parsed_args.keyword
                 if object_item is not None:
                     if 'do' not in args:
                         # by default, we want the item's get url for transclusion of raw data:
@@ -627,12 +660,13 @@
                     target = Iri(scheme='wiki.local', path=object_item, query=query, fragment=None)
                     text = object_item
                 else:
-                    target = Iri(object_url)
+                    target = Iri(scheme='wiki.local', path=object_url)
                     text = object_url
 
+                if not link_text:
+                    link_text = text
                 attrib = {xlink.href: target}
-                if link_text is not None:
-                    attrib[moin_page.alt] = link_text
+                attrib[moin_page.alt] = link_text
 
                 element = moin_page.object(attrib)
                 stack.push(element)
@@ -644,7 +678,7 @@
                     stack.top_append(text)
                 stack.pop()
                 return
-            target = Iri(link_url)
+            target = Iri(scheme='wiki.local', path=link_url)
             text = link_url
         if external_link_url:
             target = Iri(external_link_url)
--- a/MoinMoin/converter/moinwiki19_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/moinwiki19_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -129,7 +129,7 @@
                 )
             )
             (?P<url_target>
-                (%(url_schemas)s):
+                (%(uri_schemes)s):
                 \S+?
             )
             (
@@ -143,7 +143,7 @@
                 )
             )
         )
-    """ % dict(url_schemas='|'.join(config.url_schemas))
+    """ % dict(uri_schemes='|'.join(config.uri_schemes))
 
     def inline_url_repl(self, stack, url, url_target):
         url = Iri(url_target)
--- a/MoinMoin/converter/moinwiki_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/moinwiki_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -752,7 +752,7 @@
             \s*
             (
                 (?P<link_url>
-                    (%(url_schemas)s):
+                    (%(uri_schemes)s):
                     [^|]+?
                 )
                 |
@@ -779,7 +779,7 @@
             )?
             \]\]
         )
-    """ % dict(url_schemas='|'.join(config.url_schemas))
+    """ % dict(uri_schemes='|'.join(config.uri_schemes))
 
     def inline_link_repl(self, stack, link, link_url=None, link_item=None,
             link_text=None, link_args=None,
--- a/MoinMoin/converter/rst_in.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/converter/rst_in.py	Sat Jan 28 17:42:01 2012 +0100
@@ -29,7 +29,7 @@
 from MoinMoin.util.iri import Iri
 from MoinMoin.util.tree import html, moin_page, xlink
 
-from ._util import decode_data, normalize_split_text
+from ._util import allowed_uri_scheme, decode_data, normalize_split_text
 
 #### TODO: try block (do not crash if we don't have docutils)
 pytest.importorskip('docutils')
@@ -455,6 +455,8 @@
             self.close_moin_page_node()
             return
 
+        if not allowed_uri_scheme(refuri):
+            return
         self.open_moin_page_node(moin_page.a(attrib={xlink.href: refuri}))
 
     def depart_reference(self, node):
--- a/MoinMoin/items/__init__.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/items/__init__.py	Sat Jan 28 17:42:01 2012 +0100
@@ -159,6 +159,8 @@
         self.name = name
     def list_revisions(self):
         return [] # same as an empty Item
+    def destroy_all_revisions(self):
+        return True
 
 
 class Item(object):
@@ -285,7 +287,14 @@
         flaskg.clock.start('conv_dom_html')
         doc = html_conv(doc)
         flaskg.clock.stop('conv_dom_html')
-        return conv_serialize(doc, {html.namespace: ''})
+        rendered_data = conv_serialize(doc, {html.namespace: ''})
+        # This is a work-around to avoid the invalid <div /> tag from being passed
+        # and causing layout issues in many browsers
+        # Instead, send a <div></div> tag which is valid according to the HTML spec
+        # The wider issue with serialization is covered here:
+        # https://bitbucket.org/thomaswaldmann/moin-2.0/issue/145/xml-mode-serialization-returns-self
+        return "<div></div>" if rendered_data == "<div xmlns=\"http://www.w3.org/1999/xhtml\" />" \
+                             else rendered_data
 
     def _render_data_xml(self):
         doc = self.internal_representation()
@@ -654,10 +663,14 @@
     def do_get(self, force_attachment=False, mimetype=None):
         abort(404)
 
-    def _convert(self):
+    def _convert(self, doc):
         abort(404)
 
     def do_modify(self, contenttype, template_name):
+        # First, check if the current user has the required privileges
+        if not flaskg.user.may.create(self.name):
+            abort(403)
+
         # XXX think about and add item template support
         return render_template('modify_show_type_selection.html',
                                item_name=self.name,
@@ -735,6 +748,7 @@
                                rows_meta=str(ROWS_META), cols=str(COLS),
                                help=self.modify_help,
                                form=form,
+                               search_form=None,
                               )
 
     def _render_data_diff(self, oldrev, newrev):
@@ -747,7 +761,12 @@
     _render_data_diff_text = _render_data_diff
     _render_data_diff_raw = _render_data_diff
 
-    def _convert(self):
+    def _render_data_diff_atom(self, oldrev, newrev):
+        return render_template('atom.html',
+                               oldrev=oldrev, newrev=newrev, get='binary',
+                               content=Markup(self._render_data()))
+
+    def _convert(self, doc):
         return _("Impossible to convert the data to the contenttype: %(contenttype)s",
                  contenttype=request.values.get('contenttype'))
 
@@ -1045,6 +1064,15 @@
         else:
             return self._do_get(hash, force_attachment=force_attachment, mimetype=mimetype)
 
+    def _render_data_diff_atom(self, oldrev, newrev):
+        if PIL is None:
+            # no PIL, we can't do anything, we just call the base class method
+            return super(TransformableBitmapImage, self)._render_data_diff_atom(oldrev, newrev)
+        url = url_for('frontend.diffraw', _external=True, item_name=self.name, rev1=oldrev.revid, rev2=newrev.revid)
+        return render_template('atom.html',
+                               oldrev=oldrev, newrev=newrev, get='binary',
+                               content=Markup('<img src="{0}" />'.format(escape(url))))
+
     def _render_data_diff(self, oldrev, newrev):
         if PIL is None:
             # no PIL, we can't do anything, we just call the base class method
@@ -1074,8 +1102,8 @@
                 raise ValueError("content_type {0!r} not supported".format(content_type))
 
             try:
-                oldimage = PILImage.open(oldrev)
-                newimage = PILImage.open(newrev)
+                oldimage = PILImage.open(oldrev.data)
+                newimage = PILImage.open(newrev.data)
                 oldimage.load()
                 newimage.load()
                 diffimage = PILdiff(newimage, oldimage)
@@ -1122,18 +1150,25 @@
         """ convert data from storage format to memory format """
         return data.decode(config.charset).replace(u'\r\n', u'\n')
 
-    def _render_data_diff(self, oldrev, newrev):
+    def _get_data_diff_html(self, oldrev, newrev, template):
         from MoinMoin.util.diff_html import diff
         old_text = self.data_storage_to_internal(oldrev.data.read())
         new_text = self.data_storage_to_internal(newrev.data.read())
         storage_item = flaskg.storage[self.name]
         diffs = [(d[0], Markup(d[1]), d[2], Markup(d[3])) for d in diff(old_text, new_text)]
-        return Markup(render_template('diff_text.html',
-                                      item_name=self.name,
-                                      oldrev=oldrev,
-                                      newrev=newrev,
-                                      diffs=diffs,
-                                     ))
+        return render_template(template,
+                               item_name=self.name,
+                               oldrev=oldrev,
+                               newrev=newrev,
+                               diffs=diffs,
+                               )
+
+    def _render_data_diff_atom(self, oldrev, newrev):
+        """ renders diff in HTML for atom feed """
+        return self._get_data_diff_html(oldrev, newrev, 'diff_text_atom.html')
+
+    def _render_data_diff(self, oldrev, newrev):
+        return self._get_data_diff_html(oldrev, newrev, 'diff_text.html')
 
     def _render_data_diff_text(self, oldrev, newrev):
         from MoinMoin.util import diff_text
@@ -1142,6 +1177,8 @@
         difflines = diff_text.diff(oldlines, newlines)
         return '\n'.join(difflines)
 
+    _render_data_diff_raw = _render_data_diff
+
     def _render_data_highlight(self):
         from MoinMoin.converter import default_registry as reg
         data_text = self.data_storage_to_internal(self.data)
@@ -1190,6 +1227,7 @@
                                rows_data=str(ROWS_DATA), rows_meta=str(ROWS_META), cols=str(COLS),
                                help=self.modify_help,
                                form=form,
+                               search_form=None,
                               )
 
 item_registry.register(Text._factory, Type('text/*'))
@@ -1349,14 +1387,15 @@
                                rows_meta=str(ROWS_META), cols=str(COLS),
                                help=self.modify_help,
                                form=form,
+                               search_form=None,
                               )
 
     def _render_data(self):
         # TODO: this could be a converter -> dom, then transcluding this kind
         # of items and also rendering them with the code in base class could work
         item_name = self.name
-        drawing_url = url_for('frontend.get_item', item_name=item_name, member='drawing.draw')
-        png_url = url_for('frontend.get_item', item_name=item_name, member='drawing.png')
+        drawing_url = url_for('frontend.get_item', item_name=item_name, member='drawing.draw', rev=self.rev.revid)
+        png_url = url_for('frontend.get_item', item_name=item_name, member='drawing.png', rev=self.rev.revid)
         title = _('Edit drawing %(filename)s (opens in new window)', filename=item_name)
 
         mapfile = self.get_member('drawing.map')
@@ -1444,14 +1483,15 @@
                                help=self.modify_help,
                                drawing_exists=drawing_exists,
                                form=form,
+                               search_form=None,
                               )
 
     def _render_data(self):
         # TODO: this could be a converter -> dom, then transcluding this kind
         # of items and also rendering them with the code in base class could work
         item_name = self.name
-        drawing_url = url_for('frontend.get_item', item_name=item_name, member='drawing.svg')
-        png_url = url_for('frontend.get_item', item_name=item_name, member='drawing.png')
+        drawing_url = url_for('frontend.get_item', item_name=item_name, member='drawing.svg', rev=self.rev.revid)
+        png_url = url_for('frontend.get_item', item_name=item_name, member='drawing.png', rev=self.rev.revid)
         title = _('Edit drawing %(filename)s (opens in new window)', filename=self.name)
 
         mapfile = self.get_member('drawing.map')
@@ -1525,14 +1565,15 @@
                                rows_meta=str(ROWS_META), cols=str(COLS),
                                help=self.modify_help,
                                form=form,
+                               search_form=None,
                               )
 
     def _render_data(self):
         # TODO: this could be a converter -> dom, then transcluding this kind
         # of items and also rendering them with the code in base class could work
         item_name = self.name
-        drawing_url = url_for('frontend.get_item', item_name=item_name, member='drawing.svg')
-        png_url = url_for('frontend.get_item', item_name=item_name, member='drawing.png')
+        drawing_url = url_for('frontend.get_item', item_name=item_name, member='drawing.svg', rev=self.rev.revid)
+        png_url = url_for('frontend.get_item', item_name=item_name, member='drawing.png', rev=self.rev.revid)
         return Markup('<img src="{0}" alt="{1}" />'.format(png_url, drawing_url))
 
 item_registry.register(SvgDraw._factory, Type('application/x-svgdraw'))
--- a/MoinMoin/items/_tests/test_Item.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/items/_tests/test_Item.py	Sat Jan 28 17:42:01 2012 +0100
@@ -10,6 +10,11 @@
 import pytest
 
 from flask import g as flaskg
+from flask import Markup
+
+from werkzeug import escape
+
+from MoinMoin.util import diff_html
 
 from MoinMoin._tests import become_trusted, update_item
 from MoinMoin.items import Item, ApplicationXTar, NonExistent, Binary, Text, Image, TransformableBitmapImage, MarkupItem
@@ -521,9 +526,13 @@
         item1 = Binary.create(item_name)
         try:
             from PIL import Image as PILImage
-            result = TransformableBitmapImage._render_data_diff(item1, item.rev, item1.rev)
-            expected = '<img src="/+diffraw/image_Item?rev2=0" />'
-            assert str(result) == expected
+            result = Markup(TransformableBitmapImage._render_data_diff(item1, item.rev, item1.rev))
+            # On Werkzeug 0.8.2+, urls with '+' are automatically encoded to '%2B'
+            # The assert statement works with both older and newer versions of Werkzeug
+            # Probably not an intentional change on the werkzeug side, see issue:
+            # https://github.com/mitsuhiko/werkzeug/issues/146
+            assert str(result).startswith('<img src="/+diffraw/image_Item?rev') or \
+                    str(result).startswith('<img src="/%2Bdiffraw/image_Item?rev')
         except ImportError:
             # no PIL
             pass
@@ -572,6 +581,33 @@
         expected = test_text
         assert result == expected
 
+    def test__render_data_diff(self):
+        item_name = u'Html_Item'
+        empty_html = u'<span></span>'
+        html = u'<span>\ud55c</span>'
+        meta = {CONTENTTYPE: u'text/html;charset=utf-8'}
+        item = Text.create(item_name)
+        item._save(meta, empty_html)
+        item = Text.create(item_name)
+        # Unicode test, html escaping
+        rev1 = update_item(item_name, meta, html)
+        rev2 = update_item(item_name, {}, u'     ')
+        result = Text._render_data_diff(item, rev1, rev2)
+        assert escape(html) in result
+        # Unicode test, whitespace
+        rev1 = update_item(item_name, {}, u'\n\n')
+        rev2 = update_item(item_name, {}, u'\n     \n')
+        result = Text._render_data_diff(item, rev1, rev2)
+        assert '<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>' in result
+        # If fairly similar diffs are correctly spanned or not, also check indent
+        rev1 = update_item(item_name, {}, u'One Two Three Four\nSix\n\ud55c')
+        rev2 = update_item(item_name, {}, u'Two Three Seven Four\nSix\n\ud55c')
+        result = Text._render_data_diff(item, rev1, rev2)
+        assert '<span>One </span>Two Three Four' in result
+        assert 'Two Three <span>Seven </span>Four' in result
+        # Check for diff_html.diff return types
+        assert reduce(lambda x, y: x and y, [isinstance(i[1], unicode) and isinstance(i[3], unicode) for i in diff_html.diff(u'One Two Three Four\nSix\n', u'Two Three Seven Four\nSix Seven\n')], True)
+
     def test__render_data_diff_text(self):
         item_name = u'Text_Item'
         item = Text.create(item_name)
--- a/MoinMoin/mail/sendmail.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/mail/sendmail.py	Sat Jan 28 17:42:01 2012 +0100
@@ -58,16 +58,24 @@
         return str(address)
 
 
-def sendmail(to, subject, text, mail_from=None):
+def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None):
     """ Create and send a text/plain message
 
     Return a tuple of success or error indicator and message.
 
-    :param to: recipients (list)
-    :param subject: subject of email (unicode)
-    :param text: email body text (unicode)
+    :param subject: subject of email
+    :type subject: unicode
+    :param text: email body text
+    :type text: unicode
+    :param to: recipients
+    :type to: list
+    :param cc: recipients (CC)
+    :type cc: list
+    :param bcc: recipients (BCC)
+    :type bcc: list
     :param mail_from: override default mail_from
     :type mail_from: unicode
+
     :rtype: tuple
     :returns: (is_ok, Description of error or OK message)
     """
@@ -82,7 +90,7 @@
     logging.debug("send mail, from: {0!r}, subj: {1!r}".format(mail_from, subject))
     logging.debug("send mail, to: {0!r}".format(to))
 
-    if not to:
+    if not to and not cc and not bcc:
         return (1, _("No recipients, nothing to do"))
 
     subject = subject.encode(config.charset)
@@ -107,12 +115,12 @@
 
     msg.set_payload(text)
 
-    # Create message headers
-    # Don't expose emails addreses of the other subscribers, instead we
-    # use the same mail_from, e.g. u"Jürgen Wiki <noreply@mywiki.org>"
     address = encodeAddress(mail_from, charset)
     msg['From'] = address
-    msg['To'] = address
+    if to:
+        msg['To'] = ','.join(to)
+    if cc:
+        msg['CC'] = ','.join(cc)
     msg['Date'] = formatdate()
     msg['Message-ID'] = make_msgid()
     msg['Subject'] = Header(subject, charset)
@@ -120,8 +128,9 @@
     msg['Auto-Submitted'] = 'auto-generated'
 
     if cfg.mail_sendmail:
-        # Set the BCC.  This will be stripped later by sendmail.
-        msg['BCC'] = ','.join(to)
+        if bcc:
+            # Set the BCC.  This will be stripped later by sendmail.
+            msg['BCC'] = ','.join(bcc)
         # Set Return-Path so that it isn't set (generally incorrectly) for us.
         msg['Return-Path'] = address
 
@@ -144,7 +153,7 @@
                         logging.debug("could not establish a tls connection to smtp server, continuing without tls")
                     logging.debug("trying to log in to smtp server using account '{0}'".format(cfg.mail_username))
                     server.login(cfg.mail_username, cfg.mail_password)
-                server.sendmail(mail_from, to, msg.as_string())
+                server.sendmail(mail_from, (to or []) + (cc or []) + (bcc or []), msg.as_string())
             finally:
                 try:
                     server.quit()
--- a/MoinMoin/script/account/create.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/script/account/create.py	Sat Jan 28 17:42:01 2012 +0100
@@ -30,7 +30,7 @@
     )
 
     def run(self, name, display_name, email, openid, password):
-        flaskg.unprotected_storage = app.unprotected_storage
+        flaskg.unprotected_storage = app.storage
         msg = user.create_user(username=name,
                                password=password,
                                email=email,
@@ -40,5 +40,5 @@
             print msg
         else:
             u = user.User(auth_username=name)
-            print " %-20s %-25s %-35s - created." % (u.id, u.name, u.email),
+            print " %-20s %-25s %-35s - created." % (u.itemid, u.name, u.email),
 
--- a/MoinMoin/script/maint/index.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/script/maint/index.py	Sat Jan 28 17:42:01 2012 +0100
@@ -47,7 +47,7 @@
     option_list = [
         Option('--tmp', action="store_true", required=False, dest='tmp', default=False,
             help='use the temporary location.'),
-        Option('--procs', '-p', required=False, dest='procs', type=int, default=None,
+        Option('--procs', '-p', required=False, dest='procs', type=int, default=1,
             help='Number of processors the writer will use.'),
         Option('--limitmb', '-l', required=False, dest='limitmb', type=int, default=10,
             help='Maximum memory (in megabytes) each index-writer will use for the indexing pool.'),
--- a/MoinMoin/search/__init__.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/search/__init__.py	Sat Jan 28 17:42:01 2012 +0100
@@ -5,3 +5,28 @@
 MoinMoin - MoinMoin search package
 """
 
+from MoinMoin.i18n import L_
+
+from flatland import Form, String, Boolean
+from flatland.validation import Validator
+
+class ValidSearch(Validator):
+    """Validator for a valid search form
+    """
+    too_short_query_msg = L_('Search query too short.')
+
+    def validate(self, element, state):
+        if element['q'].value is None:
+            # no query, nothing to search for
+            return False
+        if len(element['q'].value) < 2:
+            return self.note_error(element, state, 'too_short_query_msg')
+        return True
+
+class SearchForm(Form):
+    q = String.using(optional=False, default=u'').with_properties(autofocus=True, placeholder=L_("Search Query"))
+    history = Boolean.using(label=L_('search also in non-current revisions'), optional=True)
+    submit = String.using(default=L_('Search'), optional=True)
+
+    validators = [ValidSearch()]
+
--- a/MoinMoin/static/js/common.js	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/static/js/common.js	Sat Jan 28 17:42:01 2012 +0100
@@ -908,3 +908,212 @@
 jQuery(document).ready(function() {
     new QuicklinksExpander();
 })
+
+function toggleSubtree(item) {
+    /* used to toggle subtrees in the subitem widget */
+    var subtree = $(item).siblings("ul");
+    subtree.toggle(200);
+}
+
+function guessContentType() {
+    /* Used in the modify_text template to guess the data content type client-side 
+     * This approach has the advantage of reacting to content type changes for the 
+     * link/transclude code without having to re-fetch the page */
+    var meta_text = $("#f_meta_text").val();
+    var ctype_regex = /["']contenttype["']\s*:\s*["']([\w-_+.]+\/[\w-_+.]+)(;|["'])/;
+    if (meta_text) {
+        var match = ctype_regex.exec(meta_text);
+        if (match) return match[1];
+    }
+    // text/plain is the default value
+    return "text/plain";
+}
+
+function transcludeSubitem(subitem_name, fullname) {
+    function moinwiki(subitem_name, fullname) {
+        return "{{" + fullname.replace("{{", "\\}}") + "}} ";
+    }
+    function mediawiki(subitem_name, fullname) {
+        return "{{:" + fullname.replace("}}", "\\}}") + "}} ";
+    }
+    function rst(subitem_name, fullname) {
+        return "\n.. include:: " + subitem_name + "\n";
+    }
+    function docbook(subitem_name, fullname) {
+        return ""; //XXX: the docbook converter currently doesn't handle transclusion with <ref> tags
+    }
+    var transclude_formats = {
+        "text/x.moin.wiki" : moinwiki,
+        "text/x.moin.creole" : moinwiki,
+        "text/x-mediawiki" : mediawiki,
+        "text/x-rst" : rst,
+        "application/docbook+xml" : docbook,
+        "text/plain" : function(x){return x + " ";},
+    }
+    var ctype = guessContentType();
+    var input_element = $("#f_data_text");
+    var ctype_format = transclude_formats[ctype];
+    if (!ctype_format) ctype_format = transclude_formats["text/plain"];
+    input_element.val(input_element.val() + ctype_format(subitem_name, fullname));
+    input_element.focus();
+}
+
+function linkSubitem(subitem_name, fullname) {
+    function moinwiki(subitem_name, fullname) {
+        return "[[" + fullname.replace("]", "\\]") + "|" + subitem_name.replace("]", "\\]") + "]] ";
+    }
+    function rst(subitem_name, fullname) {
+        return "`" + subitem_name.replace(">", "\\>").replace("`", "\\`") + " <" + fullname.replace(">", "\\>") + ">`_ ";
+    }
+    function docbook(subitem_name, fullname) {
+        return '<ulink url="/' + fullname.replace('"', '\\"') + '">' + subitem_name + "</ulink>";;
+    }
+    var link_formats = {
+        "text/x.moin.wiki" : moinwiki,
+        "text/x.moin.creole" : moinwiki,
+        "text/x-mediawiki" : moinwiki,
+        "text/x-rst" : rst,
+        "application/docbook+xml" : docbook,
+        "text/plain" : function(x){return x + " ";},
+    }
+    var ctype = guessContentType();
+    var input_element = $("#f_data_text");
+    var ctype_format = link_formats[ctype];
+    if (!ctype_format) ctype_format = link_formats["text/plain"];
+    input_element.val(input_element.val() + ctype_format(subitem_name, fullname));
+    input_element.focus();
+}
+
+function initMoinTabs($) {
+    "use strict";
+    // find all .moin-tabs elements and initialize them
+    $('.moin-tabs').each(function () {
+        var tabs = $(this),
+            titles = $(document.createElement('ul')),
+            lastLocationHash;
+        titles.addClass('moin-tab-titles');
+
+        // switching between tabs based on the current location hash
+        function updateFromLocationHash() {
+            if (location.hash !== undefined && location.hash !== '' && tabs.children(location.hash).length) {
+                if (location.hash !== lastLocationHash) {
+                    lastLocationHash = location.hash;
+                    tabs.children('.moin-tab-body').hide();
+                    tabs.children(location.hash).show();
+                    titles.children('li').children('a').removeClass('current');
+                    titles.children('li').children('a[href="' + location.hash + '"]').addClass('current');
+                }
+            } else {
+                $(titles.children('li').children('a')[0]).click();
+            }
+        }
+
+        // move all tab titles to an <ul> at the beginning of .moin-tabs
+        tabs.children('.moin-tab-title').each(function () {
+            var li = $(document.createElement('li')),
+                a = $(this).children('a');
+            a.click(function () {
+                location.hash = this.hash;
+                updateFromLocationHash();
+                return false;
+            });
+            li.append(a);
+            titles.append(li);
+            $(this).remove();
+        });
+        tabs.prepend(titles);
+
+        updateFromLocationHash();
+        setInterval(updateFromLocationHash, 40); // there is no event for that
+    });
+}
+
+jQuery(document).ready(initMoinTabs);
+
+function initMoinUsersettings($) {
+    "use strict";
+    // save initial values of each form
+    $('#moin-usersettings form').each(function () {
+        $(this).data('initialForm', $(this).serialize());
+    });
+
+    // check if any changes were made
+    function changeHandler(ev) {
+        var form = $(ev.currentTarget),
+            title = $('.moin-tab-titles a.current', form.parentsUntil('.moin-tabs').parent()),
+            e;
+        if (form.data('initialForm') === form.serialize()) {
+            // current values are identicaly to initial ones, remove all change indicators (if any)
+            $('.change-indicator', title).remove();
+        } else {
+            // the values differ
+            if (!$('.change-indicator', title).length) {
+                // only add a change indicator if there none
+                e = $(document.createElement('span'));
+                e.addClass('change-indicator');
+                e.text('*');
+                title.append(e);
+            }
+        }
+    }
+    $('#moin-usersettings form').change(changeHandler);
+
+    function submitHandler(ev) {
+        var form = $(ev.target),
+            button = $('button', form),
+            buttonBaseText = button.html(),
+            buttonDotList = [' .&nbsp;&nbsp;', ' &nbsp;.&nbsp;', ' &nbsp;&nbsp;.'],
+            buttonDotIndex = 0,
+            buttonDotAnimation;
+
+        // disable the button
+        button.attr('disabled', true);
+
+        // remove change indicators from the current tab as we are now saving it
+        $('.moin-tab-titles a.current .change-indicator',
+                form.parentsUntil('.moin-tabs').parent()).remove();
+
+        // animate the submit button to indicating a running request
+        function buttonRunAnimation() {
+            button.html(buttonBaseText + buttonDotList[buttonDotIndex % buttonDotList.length]);
+            buttonDotIndex += 1;
+        }
+        buttonDotAnimation = setInterval(buttonRunAnimation, 500);
+        buttonRunAnimation();
+
+        // send the form to the server
+        $.post(form.attr('action'), form.serialize(), function (data) {
+            var i, f, newform;
+            clearInterval(buttonDotAnimation);
+            // if the response indicates a redirect, set the new location
+            if (data.redirect) {
+                location.href = data.redirect;
+                return;
+            }
+            // remove all flash messages previously added via javascript
+            $('#moin-header .moin-flash-javascript').remove();
+            // add new flash messages from the response
+            for (i = 0; i < data.flash.length; i += 1) {
+                f = $(document.createElement('p'));
+                f.html(data.flash[i][0]);
+                f.addClass('moin-flash');
+                f.addClass('moin-flash-javascript');
+                f.addClass('moin-flash-' + data.flash[i][1]);
+                $('#moin-header').append(f);
+            }
+            // get the new form element from the response
+            newform = $(data.form);
+            // set event handlers on the new form
+            newform.submit(submitHandler);
+            newform.change(changeHandler);
+            // store the forms initial data
+            newform.data('initialForm', newform.serialize());
+            // replace the old form with the new one
+            form.replaceWith(newform);
+        }, 'json');
+        return false;
+    }
+    $('#moin-usersettings form').submit(submitHandler);
+}
+
+jQuery(document).ready(initMoinUsersettings);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/atom.html	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,62 @@
+{% import "utils.html" as utils %}
+{% macro show_meta(rev) %}
+    <div class="moin-diff-info moin-diff-info-rev-id">
+        <span class="moin-diff-info-caption">{{ _('Revision') }}:</span>
+        <span class="moin-diff-info-value">{{ rev.meta['revid'] | shorten_id }}</span>
+    </div>
+    <div class="moin-diff-info moin-diff-info-rev-size">
+        <span class="moin-diff-info-caption">{{ _('Size') }}:</span>
+        <span class="moin-diff-info-value">{{ rev.meta['size'] }}</span>
+    </div>
+    <div class="moin-diff-info moin-diff-info-rev-comment">
+        <span class="moin-diff-info-caption">{{ _('Comment') }}:</span>
+        <span class="moin-diff-info-value">{{ rev.meta['comment'] }}</span>
+    </div>
+{% endmacro %}
+
+{# A few style rules to ensure the content doesn't overflow #}
+{% macro atom_style() %}
+    <style type="text/css">
+        img {
+            max-width: 100%;
+        }
+        td {
+            padding-right: 10px;
+        }
+        table {
+            width: 100%;
+        }
+    </style>
+{% endmacro %}
+
+{%- if get == 'xml' -%}
+    <?xml-stylesheet type="text/xsl" href="{{ theme_static('atom.xslt') }}"?>
+{%- elif get == 'comment_cont_merge' -%}
+    <p style="font-size: 1.2em">…{{ comment }}</p> <br /> {{ content }}
+{%- elif get == 'first_revision' -%}
+    {{ content }}
+    <br />
+    <table class="moin-diff" cellpadding="10" width="100%">
+        <tr>
+            <td class="moin-diff-header" style="width: 100%;">
+                <strong>{{ show_meta(rev) }}</strong>
+            </td>
+        </tr>
+    </table>
+    {{ atom_style() }}
+{%- elif get == 'binary' -%}
+    {{ content }}
+    <br />
+    <table class="moin-diff" cellpadding="10" width="100%">
+        <tr>
+            <td class="moin-diff-header" style="width: 50%;">
+                <strong>{{ show_meta(oldrev) }}</strong>
+            </td>
+            <td class="moin-diff-header" style="width: 50%;">
+                <strong>{{ show_meta(newrev) }}</strong>
+            </td>
+        </tr>
+    </table>
+    {{ atom_style() }}
+{%- endif -%}
+
--- a/MoinMoin/templates/base.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/base.html	Sat Jan 28 17:42:01 2012 +0100
@@ -24,14 +24,21 @@
 
     {% block head_title %}
     <title>
-    {%- if title -%}{{ title }}{% else %}{{ item_name }}{%- endif %} -
+        {%- if title -%}
+            {{ title }}
+        {% elif title_name %}
+            {{ title_name }}
+        {% elif headline %}
+            {{ headline }}
+        {% else %}
+            {{ item_name }}
+        {%- endif %} -
     {%- if cfg.html_pagetitle -%} {{ cfg.html_pagetitle }}{% else %} {{ cfg.sitename }}{%- endif -%}
     </title>
     {% endblock %}
 
     {% block head_links %}
     <link rel="shortcut icon" href="{{ url_for('static', filename='logos/favicon.ico') }}" />
-    <link rel="archives" href="{{ url_for('frontend.history', item_name=item_name) }}" />
     {% set parent_item = theme_supp.parent_item(item_name) %}
     {%- if parent_item -%}
         <link rel="up" href="{{ url_for('frontend.show_item', item_name=parent_item) }}" />
--- a/MoinMoin/templates/delete.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/delete.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,7 +1,10 @@
 {% import "forms.html" as forms %}
 {% extends theme("layout.html") %}
+
+{% set title = _("Delete '%(item_name)s'", item_name=item.name) %}
+
 {% block content %}
-<h1>{{ _("Delete '%(item_name)s'", item_name=item.name) }}</h1>
+<h1>{{ title }}</h1>
 <div class="moin-form">
 {{ gen.form.open(form, method="post", action=url_for('frontend.delete_item', item_name=item.name)) }}
   {{ forms.render_errors(form) }}
--- a/MoinMoin/templates/destroy.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/destroy.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,8 +1,11 @@
 {% import "forms.html" as forms %}
 {% extends theme("layout.html") %}
+
+{% set title = _("DESTROY COMPLETE item '%(item_name)s'", item_name=item.name) %}
+
 {% block content %}
 {% if rev_id == None %}
-    <h1>{{ _("DESTROY COMPLETE item '%(item_name)s'", item_name=item.name) }}</h1>
+    <h1>{{ title }}</h1>
     <div class="moin-form">
     {{ gen.form.open(form, method="post", action=url_for('frontend.destroy_item', item_name=item.name)) }}
       {{ forms.render_errors(form) }}
--- a/MoinMoin/templates/diff.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/diff.html	Sat Jan 28 17:42:01 2012 +0100
@@ -4,5 +4,5 @@
 <h1>{{ _("Diff for '%(item_name)s'", item_name=newrev.item.name) }}</h1>
 <br>
 {% endblock %}
-{{ item._render_data_diff(oldrev, newrev) }}
+{{ diff_html }}
 {% endblock %}
--- a/MoinMoin/templates/diff_text.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/diff_text.html	Sat Jan 28 17:42:01 2012 +0100
@@ -3,7 +3,7 @@
 {% macro show_meta(rev) %}
     <div class="moin-diff-info moin-diff-info-rev-id">
         <span class="moin-diff-info-caption">Revision:</span>
-        <span class="moin-diff-info-value">{{ rev.meta['revid'] }}</span>
+        <span class="moin-diff-info-value">{{ rev.meta['revid'] | shorten_id }}</span>
     </div>
     <div class="moin-diff-info moin-diff-info-rev-author">
         <span class="moin-diff-info-caption">Editor:</span>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/diff_text_atom.html	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,43 @@
+{% import "utils.html" as utils %}
+
+{% macro show_meta(rev) %}
+    <div class="moin-diff-info moin-diff-info-rev-id">
+        <span class="moin-diff-info-caption">{{ _('Revision') }}:</span>
+        <span class="moin-diff-info-value">{{ rev.meta['revid'] | shorten_id }}</span>
+    </div>
+    <div class="moin-diff-info moin-diff-info-rev-size">
+        <span class="moin-diff-info-caption">{{ _('Size') }}:</span>
+        <span class="moin-diff-info-value">{{ rev.meta['size'] }}</span>
+    </div>
+    <div class="moin-diff-info moin-diff-info-rev-comment">
+        <span class="moin-diff-info-caption">{{ _('Comment') }}:</span>
+        <span class="moin-diff-info-value">{{ rev.meta['comment'] }}</span>
+    </div>
+{% endmacro %}
+<table class="moin-diff" style="width: 100%;">
+    {% for llineno, lcontent, rlineno, rcontent in diffs %}
+        <tr>
+            <td style="vertical-align: top;">{{ llineno }}:</td>
+            <td class="moin-diff-removed" style="vertical-align: top; background-color:#ff9; word-wrap: break-word;"><blockquote>{{ lcontent }}</blockquote></td>
+            <td style="vertical-align: top;">{{ rlineno }}:</td>
+            <td class="moin-diff-added" style="vertical-align: top; background-color: #cfc; word-wrap: break-word;"><blockquote>{{ rcontent }}</blockquote></td>
+        </tr>
+    {% endfor %}
+    <tr>
+        <td class="moin-diff-header-line" style="width: 3%;">
+        </td>
+        <td class="moin-diff-header" style="width: 47%;">
+            <strong>{{ show_meta(oldrev) }}</strong>
+        </td>
+        <td class="moin-diff-header-line" style="width: 3%;">
+        </td>
+        <td class="moin-diff-header" style="width: 47%;">
+            <strong>{{ show_meta(newrev) }}</strong>
+        </td>    
+    </tr>
+</table>
+<style type="text/css">
+    .moin-diff-added span { background-color: #80FF80; }
+    .moin-diff-removed span { background-color: #FFFF80; }
+</style>
+
--- a/MoinMoin/templates/forms.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/forms.html	Sat Jan 28 17:42:01 2012 +0100
@@ -20,7 +20,17 @@
     {{ gen.label(field) }}
   </dt>
   <dd>
-    {{ gen.input(field, type=field_type) }}
+    {% if field_type == 'checkbox' %}
+      {#
+      Flatland adds the value to the ID of checkboxes which we do not want as
+      the for attributes of the labels will point to a non-existent ID in that
+      case. To fix this we manually generate the ID for checkboxes here.
+      related issue in flatland: https://bitbucket.org/jek/flatland/issue/9
+      #}
+      {{ gen.input(field, type='checkbox', id='f_' + field.flattened_name()) }}
+    {% else %}
+      {{ gen.input(field, type=field_type) }}
+    {% endif %}
     {{ render_errors(field) }}
   </dd>
 {% endmacro %}
@@ -51,6 +61,14 @@
   </dd>
 {% endmacro %}
 
+{% macro render_hidden(name, value) %}
+  <input type="hidden" name="{{ name }}" value="{{ value }}" />
+{% endmacro %}
+
+{% macro render_button(text) %}
+  <button>{{ text }}</button>
+{% endmacro %}
+
 {% macro render_textcha(gen, form) %}
     {% if form.textcha_question.value %}
     <dt>
--- a/MoinMoin/templates/global_history.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/global_history.html	Sat Jan 28 17:42:01 2012 +0100
@@ -14,15 +14,13 @@
            {% set latest_timestamp = revs[0].meta['mtime'] %}
             <div class="moin-history-container"> 
                 <div class="moin-history-container-header">
-                    <span>
-                        <h2>{{ day }}</h2>
-                        {% if user.valid %}
-                        <a class="bookmark-link" href="{{ url_for('frontend.bookmark', time=latest_timestamp+1) }}">{{ _("Set bookmark") }}</a>
-                        {% endif %}
-                   </span>
+                    <h2>{{ day }}</h2>
+                    {% if user.valid %}
+                    <a class="bookmark-link" href="{{ url_for('frontend.bookmark', time=latest_timestamp+1) }}">{{ _("Set bookmark") }}</a>
+                    {% endif %}
                 </div>
                 <div class="moin-history-container-body">
-                    <table>
+                    <table class="zebra">
                     {% for rev in revs %}
                         {% set meta = rev.meta %}
                         <tr>
--- a/MoinMoin/templates/highlight.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/highlight.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,7 +1,10 @@
 {% extends theme("show.html") %}
+
+{% set title = _("Syntax highlighting for Item: %(name)s", name=item_name) %}
+
 {% block content %}
 {% block headline %}
-<h1>{{ _("Syntax highlighting for Item: %(name)s", name=item_name) }} </h1>
+<h1>{{ title }} </h1>
 {% endblock %}
 {{ data_text }}
 {% endblock %}
--- a/MoinMoin/templates/history.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/history.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,5 +1,8 @@
 {% extends theme("show.html") %}
 {% import "utils.html" as utils %}
+
+{% set title = _("History of '%(item_name)s'", item_name=item_name) %}
+
 {% block content %}
     {% set (history, next_offset, previous_offset) = history_page %}
     {% if history %}
@@ -11,57 +14,63 @@
             <a href="{{ url_for('frontend.history', item_name=item_name, offset=next_offset) }}" title="{{ _("Next") }}">&raquo;</a>
         {% endif %}
     </div>
-    <h1>{{ _("History of '%(item_name)s'", item_name=item_name) }}</h1>
+    <h1>{{ title }}</h1>
     <div class="moin-clr"></div>
     <form action="{{ url_for('frontend.diff', item_name=item_name) }}" method="GET">
         <div id="moin-page-history">
-        <table>
-            <tr>
-                <th>{{ _("Name") }}</th>
-                <th>{{ _("Rev.") }}</th>
-                <th>{{ _("Timestamp") }}</th>
-                <th>{{ _("Size") }}</th>
-                <th><input type="submit" value="Diff" /></th>
-                <th>{{ _("Editor") }}</th>
-                <th>{{ _("Content-Type") }}</th>
-                <th>{{ _("Comment") }}</th>
-                <th colspan="6">{{ _("Actions") }}</th>
-            </tr>
-            {% for doc in history %}
-            <tr>
-                <td class="moin-wordbreak">{{ doc.name|join(' | ') }}</td>
-                <td class="moin-integer">{{ doc.revid }}</td>
-                <td>{{ doc.mtime|datetimeformat }}</td>
-                <td class="moin-integer">{{ doc.size }}</td>
-                <td>
-                    <div class="moin-hist-rev">
-                        <input type="radio" name="rev1" value="{{ doc.revid }}" />
-                        <input type="radio" name="rev2" value="{{ doc.revid }}" />
-                    </div>
-                </td>
-                <td class="moin-wordbreak">{{ utils.editor_info(doc) }}</td>
-                <td class="moin-wordbreak">{{ doc.contenttype }}</td>
-                <td class="moin-wordbreak">{{ doc.comment }}</td>
-                <td><a href="{{ url_for('frontend.show_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('show') }}</a></td>
-                <td><a href="{{ url_for('frontend.show_item_meta', item_name=doc.name[0], rev=doc.revid) }}">{{ _('meta') }}</a></td>
-                <td><a href="{{ url_for('frontend.download_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('download') }}</a></td>
-                <td><a href="{{ url_for('frontend.highlight_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('highlight') }}</a></td>
-                {% if user.may.write(item_name) -%}
-                <td><a href="{{ url_for('frontend.revert_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('revert') }}</a></td>
-                {%- endif %}
-                {% if user.may.destroy(item_name) -%}
-                <td><a href="{{ url_for('frontend.destroy_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('destroy') }}</a></td>
-                {%- endif %}
-            </tr>
-            {% endfor %}
-            {% if bookmark_time %}
-            <tr> 
-                <td colspan="2">Bookmark is set to</td>
-                <td>{{ bookmark_time|datetimeformat }}</td>
-                <td colspan="11"></td>
-            </tr>
-            {% endif %}
-        </table>
+            <table class="zebra">
+                <thead>
+                    <tr>
+                        <th>{{ _("Name") }}</th>
+                        <th>{{ _("Rev.") }}</th>
+                        <th>{{ _("Timestamp") }}</th>
+                        <th>{{ _("Size") }}</th>
+                        <th><input type="submit" value="Diff" /></th>
+                        <th>{{ _("Editor") }}</th>
+                        <th>{{ _("Content-Type") }}</th>
+                        <th>{{ _("Comment") }}</th>
+                        <th colspan="6">{{ _("Actions") }}</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {% for doc in history %}
+                    <tr>
+                        <td class="moin-wordbreak">{{ doc.name|join(' | ') }}</td>
+                        <td class="moin-integer monospaced">{{ doc.revid | shorten_id }}</td>
+                        <td>{{ doc.mtime|datetimeformat }}</td>
+                        <td class="moin-integer">{{ doc.size }}</td>
+                        <td>
+                            <div class="moin-hist-rev">
+                                <input type="radio" name="rev1" value="{{ doc.revid }}" />
+                                <input type="radio" name="rev2" value="{{ doc.revid }}" />
+                            </div>
+                        </td>
+                        <td class="moin-wordbreak">{{ utils.editor_info(doc) }}</td>
+                        <td class="moin-wordbreak">{{ doc.contenttype }}</td>
+                        <td class="moin-wordbreak">{{ doc.comment }}</td>
+                        <td><a href="{{ url_for('frontend.show_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('show') }}</a></td>
+                        <td><a href="{{ url_for('frontend.show_item_meta', item_name=doc.name[0], rev=doc.revid) }}">{{ _('meta') }}</a></td>
+                        <td><a href="{{ url_for('frontend.download_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('download') }}</a></td>
+                        <td><a href="{{ url_for('frontend.highlight_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('highlight') }}</a></td>
+                        {% if user.may.write(item_name) -%}
+                        <td><a href="{{ url_for('frontend.revert_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('revert') }}</a></td>
+                        {%- endif %}
+                        {% if user.may.destroy(item_name) -%}
+                        <td><a href="{{ url_for('frontend.destroy_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('destroy') }}</a></td>
+                        {%- endif %}
+                    </tr>
+                    {% endfor %}
+                </tbody>
+                {% if bookmark_time %}
+                <tfoot>
+                    <tr>
+                        <td colspan="2">Bookmark is set to</td>
+                        <td>{{ bookmark_time|datetimeformat }}</td>
+                        <td colspan="11"></td>
+                    </tr>
+                </tfoot>
+                {% endif %}
+            </table>
         </div>
         </form>
     {% endif %}
--- a/MoinMoin/templates/index.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/index.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,5 +1,12 @@
 {% extends theme("layout.html") %}
 {% import "forms.html" as forms %}
+
+{% if item_name %}
+    {% set title = _("Index of subitems of '%(item_name)s'", item_name=item_name) %}
+{% else %}
+    {% set title = _("Global Index") %}
+{% endif %}
+
 {% block head_scripts %}
 {{ super() }}
     <script src="{{ url_for('serve.files', name='jquery_multi_download', filename='jquery.multiDownload.js') }}"></script>
@@ -66,7 +73,7 @@
     </div>
     <div>
     {% if item_name: %}
-        <h1>{{ _("Index of subitems of '%(item_name)s'", item_name=item_name) }}</h1>
+        <h1>{{ title }}</h1>
         <div class="moin-index-path">
             <a href="{{ url_for('frontend.index') }}" title="{{ _("Global Index") }}">{{ ("..") }}</a>
             <span class="moin-path-separator">{{ ("/") }}</span>
@@ -78,7 +85,7 @@
             {% endfor %}
         </div>
     {% else %}
-        <h1>{{ _("Global Index") }}</h1>
+        <h1>{{ title }}</h1>
     {% endif %}
     </div>
     <div class='moin-clr'></div>
--- a/MoinMoin/templates/itemviews.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/itemviews.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,81 +1,84 @@
 {% set exists = storage.has_item(item_name) %}
 <ul class="moin-itemviews">
-    {% for endpoint, label, title, check_exists in cfg.item_views if not endpoint in cfg.endpoints_excluded %}
-        {% if (not check_exists or check_exists and exists) and endpoint in [
-               'frontend.show_item', 'frontend.index',
-               'frontend.highlight_item', 'frontend.show_item_meta', 'frontend.download_item',
-               'frontend.history', 'frontend.backrefs', 'frontend.sitemap',
-               'frontend.similar_names',
-               'frontend.copy_item',
-           ] -%}
-            <li>
-            <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow"> {{ label }}</a>
-            </li>
-        {%- endif %}
-
-		{% if endpoint in [
-            'frontend.modify_item', 'frontend.rename_item', 'frontend.delete_item', 'frontend.rename_item'
-           ] and user.may.write(item_name) -%}
-            <li>
-            <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow"> {{ label }}</a>
-            </li>
-        {%- endif %}
-        
-        {% if endpoint =='frontend.destroy_item' and user.may.destroy(item_name) -%}
-            <li>
-            <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow"> {{ label }}</a>
-            </li>
-        {%- endif %}
-
-        {% if endpoint in [
-            'frontend.global_history', 'frontend.global_index', 'frontend.global_tags',
-            'admin.index',
-           ] -%}
-            <li>
-            <a href="{{ url_for(endpoint) }}" title="{{ title }}" rel="nofollow"> {{ label }}</a>
-            </li>
-        {%- endif %}
+    {%- for endpoint, label, title, check_exists in cfg.item_views if not endpoint in cfg.endpoints_excluded %}
+        {%- if not check_exists or check_exists and exists %}
+            {%- if endpoint in [
+                'frontend.show_item', 'frontend.index', 'frontend.highlight_item',
+                'frontend.show_item_meta', 'frontend.download_item',
+                'frontend.history', 'frontend.backrefs', 'frontend.sitemap',
+                'frontend.similar_names', 'frontend.copy_item',
+            ] %}
+                <li>
+                    <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow">{{ label }}</a>
+                </li>
+            {%- endif %}
 
-        {% if endpoint == 'frontend.quicklink_item' and user.valid -%}
-            <li>
-            <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow">
-                {% if user.isQuickLinkedTo([item_name]) -%}
-                    {{ _('Remove Link') }}
-                {% else -%}
-                    {{ _('Add Link') }}
-                {%- endif %}
-            </a>
-            </li>
+            {%- if endpoint in [
+                'frontend.modify_item', 'frontend.rename_item', 'frontend.delete_item',
+            ] and user.may.write(item_name) %}
+                <li>
+                    <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow">{{ label }}</a>
+                </li>
+            {%- endif %}
+
+            {%- if endpoint == 'frontend.destroy_item' and user.may.destroy(item_name) %}
+                <li>
+                    <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow">{{ label }}</a>
+                </li>
+            {%- endif %}
+
+            {%- if endpoint in [
+                'frontend.global_history', 'frontend.global_index', 'frontend.global_tags',
+                'admin.index',
+            ] %}
+                <li>
+                    <a href="{{ url_for(endpoint) }}" title="{{ title }}" rel="nofollow">{{ label }}</a>
+                </li>
+            {%- endif %}
+
+            {%- if endpoint == 'frontend.quicklink_item' and user.valid %}
+                <li>
+                    <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow">
+                        {%- if user.isQuickLinkedTo([item_name]) %}
+                            {{ _('Remove Link') }}
+                        {%- else %}
+                            {{ _('Add Link') }}
+                        {%- endif %}
+                    </a>
+                </li>
+            {%- endif %}
+
+            {%- if endpoint == 'frontend.subscribe_item' and user.valid %}
+                <li>
+                    <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow">
+                        {%- if user.isSubscribedTo([item_name]) %}
+                            {{ _('Unsubscribe') }}
+                        {%- else %}
+                            {{ _('Subscribe') }}
+                        {%- endif %}
+                    </a>
+                </li>
+            {%- endif %}
+
+            {%- if endpoint == 'special.comments' %}
+                <li class="toggleCommentsButton" style="display:none;">
+                    <a href="#" onClick="toggleComments();return false;" title="{{ title }}">{{ label }}</a>
+                </li>
+            {%- endif %}
+
+            {%- if endpoint == 'special.supplementation' %}
+                {%- for sub_item_name in cfg.supplementation_item_names %}
+                    {%- set current_sub = item_name.rsplit('/', 1)[-1] %}
+                    {%- if not current_sub in cfg.supplementation_item_names %}
+                        {%- set supp_name = '%s/%s' % (item_name, sub_item_name) %}
+                        {%- if storage.has_item(supp_name) or user.may.write(supp_name) %}
+                            <li>
+                                <a href="{{ url_for('frontend.show_item', item_name=supp_name) }}" rel="nofollow">{{ _(sub_item_name) }}</a>
+                            </li>
+                        {%- endif %}
+                    {%- endif %}
+                {%- endfor %}
+            {%- endif %}
         {%- endif %}
-        {% if endpoint == 'frontend.subscribe_item' and user.valid -%}
-            <li>
-            <a href="{{ url_for(endpoint, item_name=item_name) }}" title="{{ title }}" rel="nofollow">
-                {% if user.isSubscribedTo([item_name]) %}
-                        {{ _('Unsubscribe') }}
-                {% else %}
-                        {{ _('Subscribe') }}
-                {% endif %}
-            </a>
-            </li>
-        {%- endif %}
-
-        {% if endpoint == 'special.comments' -%}
-            <li class="toggleCommentsButton" style="display:none;">
-            <a href="#" onClick="toggleComments();return false;" title="{{ title }}">{{ label }}</a>
-            </li>
-        {%- endif %}
-        {% if endpoint == 'special.supplementation' -%}
-            {%- for sub_item_name in cfg.supplementation_item_names -%}
-                {% set current_sub = item_name.rsplit('/', 1)[-1] %}
-                {%- if not current_sub in cfg.supplementation_item_names -%}
-                    {% set supp_name = '%s/%s' % (item_name, sub_item_name) -%}
-                    {% if storage.has_item(supp_name) or user.may.write(supp_name) %}
-                        <li>
-                            <a href="{{ url_for('frontend.show_item', item_name=supp_name) }}" rel="nofollow">{{ _(sub_item_name) }}</a>
-                        </li>
-                    {%- endif %}
-                {%- endif %}
-            {%- endfor -%}
-        {%- endif %}
-    {% endfor %}
+    {%- endfor %}
 </ul>
--- a/MoinMoin/templates/layout.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/layout.html	Sat Jan 28 17:42:01 2012 +0100
@@ -18,15 +18,15 @@
 
 <div id="moin-header">
 {% block header %}
-    {% if search_form %} 
+    {% if search_form %}
     {{ gen.form.open(search_form, id='moin-searchform', method='get', action=url_for('frontend.search')) }}
         <div>
             {{ gen.input(search_form['q'], type='search', id='moin-search-query', size='40') }}
-            {{ gen.input(search_form['submit'], type='submit', id='moin-search-submit') }}
+            {{ gen.button(search_form['submit'], type='submit', id='moin-search-submit') }}
             {{ forms.render_errors(search_form) }}
         </div>
     {{ gen.form.close() }}
-    {% endif %} 
+    {% endif %}
 
     {% if logo %}
     <div id="moin-logo">
@@ -144,6 +144,9 @@
 {{ after_header }}
 
 <div id="moin-page" lang="{{ theme_supp.content_lang }}" dir="{{ theme_supp.content_dir }}">
+
+    {% block subitem_navigation %}{% endblock %}
+
     {% block item -%}
         {# If you want itemviews in your template, extend from show.html, not from here. #}
         <div id="moin-content">
--- a/MoinMoin/templates/meta.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/meta.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,12 +1,14 @@
 {% extends theme("show.html") %}
 
+{% set title = _("Metadata of '%(item_name)s'", item_name=item_name) %}
+
 {% block rev_navigation %}
     {{ rev_navigation(view='frontend.show_item_meta') }}
 {% endblock %}
 
 {% block headline %}
 <h1>
-    {{ _("Metadata of '%(item_name)s'", item_name=item_name) }}
+    {{ title }}
     {% if show_revision %}({{ _("Revision") }} {{ rev.revid }}){% endif %}
 </h1>
 {% endblock %}
--- a/MoinMoin/templates/modify_applet.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/modify_applet.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,7 +1,10 @@
 {% import "forms.html" as forms %}
 {% extends theme("layout.html") %}
+
+{% set title = _("Modifying %(item_name)s", item_name=item_name) %}
+
 {% block content %}
-<h1>{{ _("Modifying %(item_name)s", item_name=item_name) }}</h1>
+<h1>{{ title }}</h1>
 <div class="moin-form">
 {{ gen.form.open(form, method='post', action='', enctype='multipart/form-data') }}
 {{ forms.render_errors(form) }}
--- a/MoinMoin/templates/modify_show_type_selection.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/modify_show_type_selection.html	Sat Jan 28 17:42:01 2012 +0100
@@ -4,10 +4,10 @@
 <p>
 {{ _("This item does not exist (yet), but you can try creating it now. Please select the type of the item you want to create.") }}
 </p>
-<table>
+<table class="zebra">
     {% for gname, contenttypes in contenttype_groups %}
     <tr>
-        <td class="green">{{ gname }}</td>
+        <th>{{ gname }}</th>
     </tr>
     <tr>
         <td>
--- a/MoinMoin/templates/modify_text.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/modify_text.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,4 +1,18 @@
 {% extends "modify_binary.html" %}
+{% import "utils.html" as utils %}
+
+{% block subitem_navigation %}
+        {% call(fullname, shortname, contenttype, has_children) utils.render_subitem_navigation(item_name, True) %}
+            {% set shortname = shortname|json_dumps %}
+            {% set fullname = fullname|json_dumps %}
+            <button class="link-action" onclick='linkSubitem({{ shortname }}, {{ fullname }})'
+                title="{{ _('Link to Subitem') }}">{{ _('Link') }}</button>
+            <button class="transclude-action" 
+                onclick='transcludeSubitem({{ shortname }}, {{ fullname }})'
+                title="{{ _('Transclude Subitem') }}">{{ _('Transclude') }}</button>
+        {% endcall %}
+{% endblock %}
+
 {% block data_editor %}
 {{ gen.textarea(form['data_text'], lang=lang, dir=direction, rows=rows_data, cols=cols) }}
 <br />
--- a/MoinMoin/templates/rename.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/rename.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,7 +1,10 @@
 {% import "forms.html" as forms %}
 {% extends theme("layout.html") %}
+
+{% set title = _("Rename '%(item_name)s'", item_name=item.name) %}
+
 {% block content %}
-<h1>{{ _("Rename '%(item_name)s'", item_name=item.name) }}</h1>
+<h1>{{ title }}</h1>
 <div class="moin-form">
 {{ gen.form.open(form, method="post", action=url_for('frontend.rename_item', item_name=item.name)) }}
   {{ forms.render_errors(form) }}
--- a/MoinMoin/templates/show.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/show.html	Sat Jan 28 17:42:01 2012 +0100
@@ -36,6 +36,10 @@
     </ul>
 {% endmacro %}
 
+{% block subitem_navigation %}
+    {{ utils.render_subitem_navigation(item_name, False) }}
+{% endblock %}
+
 {% block content %}
     {% if show_navigation %}
         {% block rev_navigation %}
@@ -64,7 +68,7 @@
 {% block footer_meta %}
     {% if rev %}
     <p id="moin-pageinfo" lang="{{ theme_supp.ui_lang }}" dir="{{ theme_supp.ui_dir }}">
-        {% if cfg.show_interwiki %}{{ cfg.interwikiname }}:{% endif %}{{ item_name }} (rev {{rev.revid}}),
+        {% if cfg.show_interwiki %}{{ cfg.interwikiname }}:{% endif %}{{ item_name }} (rev {{rev.revid | shorten_id}}),
         {{ _("modified") }} {{ rev.meta['mtime']|datetimeformat }}
         {{ _("by") }} {{ utils.editor_info(rev.meta) }}{% if rev.tags %},
         {{ _("tagged") }}
--- a/MoinMoin/templates/sitemap.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/sitemap.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,6 +1,9 @@
 {% extends theme("layout.html") %}
+
+{% set title = _("SiteMap of %(item_name)s", item_name=item_name) %}
+
 {% block content %}
-<h1>{{ _("SiteMap of %(item_name)s", item_name=item_name) }}</h1>
+<h1>{{ title }}</h1>
 {% for entry in sitemap recursive %}
     {% if isinstance(entry, list) %}
         <ul>
--- a/MoinMoin/templates/usersettings.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/usersettings.html	Sat Jan 28 17:42:01 2012 +0100
@@ -1,101 +1,42 @@
 {% extends theme("layout.html") %}
-{% import "forms.html" as forms %}
+{% import "usersettings_forms.html" as user_forms %}
 
 {% block item %}
-{% if part == 'main' %}
-<h1>{{ _("User Settings") }}</h1>
-<ul>
-    <li><a href="{{ url_for('frontend.usersettings', part='personal') }}">{{ _("Personal Settings") }}</a></li>
-    <li><a href="{{ url_for('frontend.usersettings', part='password') }}">{{ _("Change password") }}</a></li>
-    <li><a href="{{ url_for('frontend.usersettings', part='notification') }}">{{ _("Notification Settings") }}</a></li>
-    <li><a href="{{ url_for('frontend.usersettings', part='ui') }}">{{ _("Wiki Appearance Settings") }}</a></li>
-    <li><a href="{{ url_for('frontend.usersettings', part='navigation') }}">{{ _("Navigation Settings") }}</a></li>
-    <li><a href="{{ url_for('frontend.usersettings', part='options') }}">{{ _("Options") }}</a></li>
-</ul>
+<div id="moin-content">
+    <h1>{{ _("User Settings") }}</h1>
 
-{% elif part == 'personal' %}
-<h1>{{ _("Personal Settings") }}</h1>
-<div class="moin-form">
-{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings', part=part)) }}
-  {{ forms.render_errors(form) }}
-  <dl>
-    {{ forms.render_field(gen, form['name'], 'text') }}
-    {{ forms.render_field(gen, form['display_name'], 'text') }}
-    {{ forms.render_field(gen, form['openid'], 'url') }}
-    {{ forms.render_select(gen, form['timezone']) }}
-    {{ forms.render_select(gen, form['locale']) }}
-  </dl>
-  {{ gen.input(form['submit'], type='submit') }}
-{{ gen.form.close() }}
-</div>
+    <div id="moin-usersettings" class="moin-tabs">
+        <h2 class="moin-tab-title"><a href="#personal">{{ _("Personal Settings") }}</a></h2>
+        <div id="personal" class="moin-tab-body moin-form">
+            {{ user_forms.personal(form_objs.personal) }}
+        </div>
 
-{% elif part == 'password' %}
-<h1>{{ _("Change Password") }}</h1>
-<div class="moin-form">
-{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings', part=part)) }}
-  {{ forms.render_errors(form) }}
-  <dl>
-    {{ forms.render_field(gen, form['password_current'], 'password') }}
-    {{ forms.render_field(gen, form['password1'], 'password') }}
-    {{ forms.render_field(gen, form['password2'], 'password') }}
-  </dl>
-  {{ gen.input(form['submit'], type='submit') }}
-{{ gen.form.close() }}
+        <h2 class="moin-tab-title"><a href="#password">{{ _("Change Password") }}</a></h2>
+        <div id="password" class="moin-tab-body moin-form">
+            {{ user_forms.password(form_objs.password) }}
+        </div>
+
+        <h2 class="moin-tab-title"><a href="#notification">{{ _("Notification Settings") }}</a></h2>
+        <div id="notification" class="moin-tab-body moin-form">
+            {{ user_forms.notification(form_objs.notification) }}
+        </div>
+
+        <h2 class="moin-tab-title"><a href="#ui">{{ _("Wiki Appearance Settings") }}</a></h2>
+        <div id="ui" class="moin-tab-body moin-form">
+            {{ user_forms.ui(form_objs.ui) }}
+        </div>
+
+        <h2 class="moin-tab-title"><a href="#navigation">{{ _("Navigation Settings") }}</a></h2>
+        <div id="navigation" class="moin-tab-body moin-form">
+            {{ user_forms.navigation(form_objs.navigation) }}
+        </div>
+
+        <h2 class="moin-tab-title"><a href="#options">{{ _("Options") }}</a></h2>
+        <div id="options" class="moin-tab-body moin-form">
+            {{ user_forms.options(form_objs.options) }}
+        </div>
+    </div>
 </div>
 
-{% elif part == 'notification' %}
-<h1>{{ _("Notification Settings") }}</h1>
-<div class="moin-form">
-{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings', part=part)) }}
-  {{ forms.render_errors(form) }}
-  <dl>
-    {{ forms.render_field(gen, form['email'], 'email') }}
-  </dl>
-  {{ gen.input(form['submit'], type='submit') }}
-{{ gen.form.close() }}
-</div>
-
-{% elif part == 'ui' %}
-<h1>{{ _("Wiki Appearance Settings") }}</h1>
-<div class="moin-form">
-{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings', part=part)) }}
-  {{ forms.render_errors(form) }}
-  <dl>
-    {{ forms.render_select(gen, form['theme_name']) }}
-    {{ forms.render_field(gen, form['css_url'], 'url') }}
-    {{ forms.render_field(gen, form['edit_rows'], 'text') }}
-    {{ forms.render_field(gen, form['results_per_page'], 'number') }}
-  </dl>
-  {{ gen.input(form['submit'], type='submit') }}
-{{ gen.form.close() }}
-</div>
-
-{% elif part == 'navigation' %}
-<h1>{{ _("Navigation Settings") }}</h1>
-<div class="moin-form">
-{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings', part=part)) }}
-  {{ forms.render_errors(form) }}
-  <dl>
-    {# TODO: find a good way to handle quicklinks #}
-  </dl>
-  {{ gen.input(form['submit'], type='submit') }}
-{{ gen.form.close() }}
-</div>
-
-{% elif part == 'options' %}
-<h1>{{ _("Options") }}</h1>
-<div class="moin-form">
-{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings', part=part)) }}
-  {{ forms.render_errors(form) }}
-  <dl>
-    {{ forms.render_field(gen, form['mailto_author'], 'checkbox') }}
-    {{ forms.render_field(gen, form['edit_on_doubleclick'], 'checkbox') }}
-    {{ forms.render_field(gen, form['show_comments'], 'checkbox') }}
-    {{ forms.render_field(gen, form['disabled'], 'checkbox') }}
-  </dl>
-  {{ gen.input(form['submit'], type='submit') }}
-{{ gen.form.close() }}
-</div>
-{% endif %}
 {% endblock %}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/usersettings_ajax.html	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,15 @@
+{% import "usersettings_forms.html" as user_forms %}
+
+{% if part == 'personal' %}
+    {{ user_forms.personal(form) }}
+{% elif part == 'password' %}
+    {{ user_forms.password(form) }}
+{% elif part == 'notification' %}
+    {{ user_forms.notification(form) }}
+{% elif part == 'ui' %}
+    {{ user_forms.ui(form) }}
+{% elif part == 'navigation' %}
+    {{ user_forms.navigation(form) }}
+{% elif part == 'options' %}
+    {{ user_forms.options(form) }}
+{% endif %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/usersettings_forms.html	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,83 @@
+{% import "forms.html" as forms %}
+
+{% macro personal(form) %}
+{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings')) }}
+{{ forms.render_errors(form) }}
+<dl>
+    {{ forms.render_field(gen, form['name'], 'text') }}
+    {{ forms.render_field(gen, form['display_name'], 'text') }}
+    {{ forms.render_field(gen, form['openid'], 'url') }}
+    {{ forms.render_select(gen, form['timezone']) }}
+    {{ forms.render_select(gen, form['locale']) }}
+</dl>
+{{ forms.render_hidden('part', 'personal') }}
+{{ forms.render_button(_("Save")) }}
+{{ gen.form.close() }}
+{% endmacro %}
+
+
+{% macro password(form) %}
+{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings')) }}
+{{ forms.render_errors(form) }}
+<dl>
+    {{ forms.render_field(gen, form['password_current'], 'password') }}
+    {{ forms.render_field(gen, form['password1'], 'password') }}
+    {{ forms.render_field(gen, form['password2'], 'password') }}
+</dl>
+{{ forms.render_hidden('part', 'password') }}
+{{ forms.render_button(_("Change password")) }}
+{{ gen.form.close() }}
+{% endmacro %}
+
+{% macro notification(form) %}
+{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings')) }}
+{% if cfg.user_email_verification %}
+<p>{{ _("Changing your email address requires you to verify it. A link will be sent to you.") }}</p>
+{% endif %}
+{{ forms.render_errors(form) }}
+<dl>
+    {{ forms.render_field(gen, form['email'], 'email') }}
+</dl>
+{{ forms.render_hidden('part', 'notification') }}
+{{ forms.render_button(_("Save")) }}
+{{ gen.form.close() }}
+{% endmacro %}
+
+{% macro ui(form) %}
+{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings')) }}
+{{ forms.render_errors(form) }}
+<dl>
+    {{ forms.render_select(gen, form['theme_name']) }}
+    {{ forms.render_field(gen, form['css_url'], 'url') }}
+    {{ forms.render_field(gen, form['edit_rows'], 'text') }}
+    {{ forms.render_field(gen, form['results_per_page'], 'text') }}
+</dl>
+{{ forms.render_hidden('part', 'ui') }}
+{{ forms.render_button(_("Save")) }}
+{{ gen.form.close() }}
+{% endmacro %}
+
+{% macro navigation(form) %}
+{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings')) }}
+{{ forms.render_errors(form) }}
+<dl>
+    {# TODO: find a good way to handle quicklinks #}
+</dl>
+{{ forms.render_hidden('part', 'navigation') }}
+{{ forms.render_button(_("Save")) }}
+{{ gen.form.close() }}
+{% endmacro %}
+
+{% macro options(form) %}
+{{ gen.form.open(form, method="post", action=url_for('frontend.usersettings')) }}
+{{ forms.render_errors(form) }}
+<dl>
+    {{ forms.render_field(gen, form['mailto_author'], 'checkbox') }}
+    {{ forms.render_field(gen, form['edit_on_doubleclick'], 'checkbox') }}
+    {{ forms.render_field(gen, form['show_comments'], 'checkbox') }}
+    {{ forms.render_field(gen, form['disabled'], 'checkbox') }}
+</dl>
+{{ forms.render_hidden('part', 'options') }}
+{{ forms.render_button(_("Save")) }}
+{{ gen.form.close() }}
+{% endmacro %}
--- a/MoinMoin/templates/utils.html	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/templates/utils.html	Sat Jan 28 17:42:01 2012 +0100
@@ -22,11 +22,11 @@
 
 
 {% macro table(headings, rows) %}
-<table>
+<table class="zebra">
 <thead>
     <tr>
         {% for heading in headings %}
-        <td><strong>{{ heading }}</strong></td>
+        <th>{{ heading }}</th>
         {% endfor %}
     </tr>
 </thead>
@@ -42,3 +42,39 @@
 </table>
 {% endmacro %}
 
+{% macro _render_subitem_navigation_tree(subitems, newtab, parentcaller) %}
+    <ul>
+        {% for fullname, shortname, contenttype, has_children in subitems %}
+            <li>
+                {# call our parent's caller with all the data we have if they exist, used to implement
+                    transclude/link actions in the modify view #}
+                {% if parentcaller %}
+                    {{ parentcaller(fullname, shortname, contenttype, has_children) }}
+                {% endif %}
+                <a href="{{ url_for('frontend.show_item', item_name=fullname) }}"
+                    title="{{ shortname }}" class="subitem-link"
+                    {% if newtab %}target="_blank"{% endif %}>{{ shortname }}</a>
+                {% if has_children %}
+                    <button class="expander" title="{{ _('Expand Subitem') }}"
+                        onclick="toggleSubtree(this)">/</button>
+                    {{ _render_subitem_navigation_tree(theme_supp.subitem_index(fullname), newtab, parentcaller) }}
+                {% endif %}
+            </li>
+        {% endfor %}
+    </ul>
+{% endmacro %}
+
+{% macro render_subitem_navigation(itemname, newtab) %}
+    {% set subitems = theme_supp.subitem_index(itemname) %}
+    {% if caller %}
+        {% set mycaller = caller %}
+    {% endif %}
+    {% if subitems %}
+        <div class="moin-subitem-navigation">
+            <div class="list-header">
+                {{ _('Subitems') }} 
+            </div>
+            {{ _render_subitem_navigation_tree(subitems, newtab, mycaller) }}
+        </div>
+    {% endif %}
+{% endmacro %}
--- a/MoinMoin/themes/__init__.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/themes/__init__.py	Sat Jan 28 17:42:01 2012 +0100
@@ -10,6 +10,9 @@
 
 import urllib
 
+from json import dumps
+from operator import itemgetter
+
 from flask import current_app as app
 from flask import g as flaskg
 from flask import url_for, request
@@ -21,6 +24,7 @@
 from MoinMoin.i18n import _, L_, N_
 from MoinMoin import wikiutil, user
 from MoinMoin.config import USERID, ADDRESS, HOSTNAME
+from MoinMoin.search import SearchForm
 from MoinMoin.util.interwiki import split_interwiki, getInterwikiHome, is_local_wiki, is_known_wiki, url_for_item
 from MoinMoin.util.crypto import cache_key
 from MoinMoin.util.forms import make_generator
@@ -113,6 +117,20 @@
             breadcrumbs.append((wiki_name, item_name, href, exists, err))
         return breadcrumbs
 
+    def subitem_index(self, item_name):
+        """
+        Get a list of subitems for the given item_name
+
+        :rtype: list
+        :returns: list of item tuples (item_name, item_title, item_mime_type, has_children)
+        """
+        from MoinMoin.items import Item
+        item = Item.create(item_name)
+        item_index = item.get_detailed_index(item.flat_index())
+        # Sort items by whether or not they have children, then by name:
+        item_index = sorted(item_index, key=itemgetter(-1, 0))
+        return item_index
+
     def userhome(self):
         """
         Assemble arguments used to build user homepage link
@@ -316,7 +334,6 @@
         result['email'] = email
     return result
 
-
 def shorten_item_name(name, length=25):
     """
     Shorten item names
@@ -339,6 +356,20 @@
             name = u'{0}...{1}'.format(name[:half + left], name[-half:])
     return name
 
+def shorten_id(name, length=7):
+    """
+    Shorten IDs to specified length
+
+    Shorten long IDs into just the first <length> characters. There's
+    no need to display the whole IDs everywhere.
+
+    :param name: item name, unicode
+    :param length: Maximum length of the resulting ID, int
+    :rtype: unicode
+    :returns: <name> truncated to <length> characters
+    """
+
+    return name[:length]
 
 MIMETYPE_TO_CLASS = {
     'application/pdf': 'pdf',
@@ -374,7 +405,9 @@
 
 def setup_jinja_env():
     app.jinja_env.filters['shorten_item_name'] = shorten_item_name
+    app.jinja_env.filters['shorten_id'] = shorten_id
     app.jinja_env.filters['contenttype_to_class'] = contenttype_to_class
+    app.jinja_env.filters['json_dumps'] = dumps
     # please note that these filters are installed by flask-babel:
     # datetimeformat, dateformat, timeformat, timedeltaformat
 
@@ -395,5 +428,6 @@
                             'get_editor_info': lambda meta: get_editor_info(meta),
                             'utctimestamp': lambda dt: utctimestamp(dt),
                             'gen': make_generator(),
+                            'search_form': SearchForm.from_defaults(),
                             })
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/themes/modernized/static/atom.xslt	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:atom="http://www.w3.org/2005/Atom"
+  xmlns:dc="http://purl.org/dc/elements/1.1/" >
+    <xsl:output method="html" />
+    <xsl:template match="/">
+        <html>
+            <head>
+                <title><xsl:value-of select="/atom:feed/atom:title" /></title>
+            </head>
+            <body>
+                <xsl:apply-templates select="atom:feed" />
+                <script type="text/javascript"><![CDATA[
+                    var entries = document.getElementsByTagName('div');
+
+                    var elements = [];
+                    for (i = 0; i < entries.length; i++)
+                    {
+                        if (entries[i].className != 'summary')
+                            continue;
+                            
+                        elements[elements.length] = entries[i];
+                    }
+                    
+                    for (i = 0; i < elements.length; i++)
+                        elements[i].innerHTML = elements[i].innerText;
+                ]]></script>
+            </body>
+        </html>
+    </xsl:template>
+    <xsl:template match="atom:feed">
+        <div style="color: red;">This is just a fallback version, please use a proper atom feed reader</div>
+        <div style="font-size: 2.4em; text-align: center;"><xsl:value-of select="atom:title" /></div>
+        <div id="entries">
+            <xsl:apply-templates select="atom:entry" />
+        </div>
+    </xsl:template>
+    <xsl:template match="atom:feed/atom:entry">
+        <div style="margin: 2em; border-top: solid 1px #ddd; padding: 1em 0;">
+            <div style="font-size: 1.4em;"><xsl:value-of select="atom:title" /></div>
+            <div style="font-size: 0.9em; color: #999;"><xsl:value-of select="atom:author/atom:name" />, <xsl:value-of select="atom:updated" /></div>
+            <div style="padding: 0px 30px 0px 30px;" class="summary"><xsl:value-of select="atom:summary" /></div>
+        </div>
+    </xsl:template>
+</xsl:stylesheet>
+
--- a/MoinMoin/themes/modernized/static/css/common.css	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/themes/modernized/static/css/common.css	Sat Jan 28 17:42:01 2012 +0100
@@ -53,13 +53,13 @@
 .moin-integer { text-align: right; padding-right: 1em; }
 
 /* headings - from: http: //www.w3.org/TR/CSS2/sample.html */
-h1 { font-size: 2em; margin: .67em 0; }
-h2 { font-size: 1.5em; margin: .75em 0; }
-h3 { font-size: 1.17em; margin: .83em 0; }
-h4 { margin: 1.12em 0; }
-h5 { font-size: .83em; margin: 1.5em 0; }
-h6 { font-size: .75em; margin: 1.67em 0; }
-
+h1 { font-size: 2em; margin: .67em 0; border-bottom: 5px solid #4D7DA9; padding-bottom: 5px; }
+h2 { font-size: 1.5em; margin: .75em 0; padding-bottom: 4px; }
+h3 { font-size: 1.17em; margin: .83em 0; padding-bottom:3px; }
+h4 { margin: 1.12em 0; padding-bottom:2px; }
+h5 { font-size: .83em; margin: 1.5em 0; padding-bottom: 2px; }
+h6 { font-size: .75em; margin: 1.67em 0; padding-bottom:2px; }
+h2,h3,h4,h5,h6{border-bottom: 3px solid #4D7DA9; }
 /* Links - most themes will override */
 a { text-decoration: none; }
 a:link { color: blue; }
@@ -111,13 +111,26 @@
 pre.comment { background-color: #CCC; color: red; padding: 0; margin: 0; border: 0; }
 pre.comment:before { content: url(../img/attention.png); }
 
+/* tt (teletype) replacement */
+.monospaced {
+    font-family: monospace;
+}
+
 /* tables */
 table { margin: 0.5em; border-collapse: collapse; }
-th, td { padding: 0.25em 0.5em 0.25em 0.5em; border: 1px solid #ADB9CC; vertical-align: middle; }
+th, td { padding: 0.3em 0.4em; vertical-align: middle; text-align: left; }
+th { border: 1px solid #4D7DA9; background-color: #81BBF2; }
+td { border: 1px solid #ADB9CC; }
 td p { margin: 0; padding: 0; }
+table.zebra { border: none; border-collapse: separate; border-spacing: 1px; }
+.zebra thead { background-color: #81BBF2; }
+.zebra tfoot { background-color: #C4D9FF; }
+.zebra th, .zebra td { border: none; }
+.zebra tbody tr, .zebra > tr { background-color: #EEF1F5; }
+.zebra tbody tr:nth-child(odd), .zebra > tr:nth-child(odd) { background-color: #D9DFE8; }
 
 /* TableOfContents macro */
-div.moin-table-of-contents { border: 1px solid #BBB; color: black;
+div.moin-table-of-contents { border: 1px solid #BBB; color: black; background: #fff;
             font-size: 80%; margin: 0.5em 0 0.75em 1em; padding: 0.5em 0.75em 0.5em 0.5em; text-indent: -1em;
             max-width: 35%; text-align: left; float: right; clear: both;
             box-shadow: 10px 10px 5px #679; border-radius: 15px;
@@ -150,6 +163,16 @@
 .moin-form dd input { width: 20em; }
 .moin-form dt label.required:after { content: '*'; color: gray; }
 
+/* tabs on user settings page */
+.moin-tab-titles { margin: 0; padding: -10px 0 0; list-style: none; border-bottom: 3px solid #4D7DA9; }
+.moin-tab-titles li { display: inline-block; margin: 10px 0 -3px; padding: 0 5px; border-bottom: 3px solid #4D7DA9; }
+.moin-tab-titles a { display: inline-block; padding: 4px; background-color: #81BBF2; border-width: 1px 1px 0;
+            border-style: solid; border-color: #4D7DA9; color: #222 !important; }
+.moin-tab-titles a:hover { background-color: #4D7DA9; text-decoration: none; }
+.moin-tab-titles a.current { background: #4D7DA9; padding-top: 8px; margin-top: -4px; }
+.moin-tab-titles .change-indicator { font-weight: bold; color: #D22; }
+.moin-tab-title a { color: #000 !important; text-decoration: none !important; }
+
 /* Recent changes */
 .rcrss { float: right; margin: 0 7px 0 14px; height: 0; position: relative; top: 9px; }
 .recentchanges table { clear: both; border-collapse: collapse; border: 1px solid #4D7DA9; }
@@ -211,11 +234,6 @@
 .moin-hist-rev { margin: 0; }
 #moin-global-history,
 #moin-page-history { font-size: 75%; }
-#moin-page-history tr { border: 1px solid #4D7DA9; } 
-#moin-global-history th,
-#moin-page-history th { background-color: #81BBF2; border: 0px; font-size: 1.12em; padding: 6px .5em; text-align: left; }
-#moin-global-history td {  border: 0px; vertical-align: top;text-align: left; padding: 0.4em 0.3em; }
-#moin-page-history td { background-color: #E6EAF0; border: 0px; vertical-align: top; }
 .moin-wordbreak { word-break: break-all;  word-wrap: break-word; } /* Firefox needs javascript assistance within tables */
 .moin-history-time { width: 10%; }
 .moin-history-contenttype { width: 18%; }
@@ -223,11 +241,12 @@
 .moin-history-comment { width: 30%; }
 .moin-action { width: 2%; }
 .moin-action a:hover { text-decoration: none; }
-.moin-history-container { border: 1px solid #4D7DA9; }
+.moin-history-container { border: 1px solid #4D7DA9; margin: 2em 0; }
 .moin-history-container-header { background: #81BBF2; margin: 0px; padding: 4px; color: #3b3131; border-bottom: 1px solid #4D7DA9; }
-.moin-history-container-header h2 { display: inline; }
+.moin-history-container-header h2 { display: inline; border: none; }
 .moin-history-container-header a.bookmark-link { margin-left: 10px; color: #3b3131; }
-.moin-history-container-body { padding: 4px; font-size: 14px; }
+.moin-history-container-body { margin: 0; padding: 0; font-size: 14px; }
+.moin-history-container-body table { margin: 0; width: 100%; }
 .moin-history-save:before { content: url('../img/moin-edit.png'); }
 .moin-history-trash:before { content: url('../img/moin-deleted.png'); }
 .moin-history-rename:before { content: url('../img/moin-renamed.png'); }
@@ -244,7 +263,6 @@
 .moin-clr { clear: both; }
 
 /* item index page */
-#moin-content h1 { display: inline; }
 .moin-select-all { padding: 0.5em; color: #342D7E; background: #FFFFFF; border: 1px solid #E5E5E5; text-align: left; }
 .moin-select-actions { position: relative; margin: 0 1em; padding: 0; background: #FFFFFF; z-index: 2; white-space: nowrap; color: #342D7E; text-align: left; }
 .moin-select-actions div { margin: 0; padding: 0.5em; border: 1px groove #E5E5E5; }
@@ -548,9 +566,10 @@
 body { background-color: #F3F7FD; }
 
 /* moin-content */
-#moin-page { margin: 0; padding: 2px 20px 20px; box-shadow: 1px 1px 33px -11px inset;
-/* support for firefox 3.5/3.6 and safari */
--moz-box-shadow: 1px 1px 33px -11px inset; -webkit-box-shadow: inset 1px 1px 33px -11px black; }
+#moin-page { margin: 0; padding: 0; display: table; width: 100%; table-layout: fixed; }
+#moin-content { background: #FFF; overflow: hidden; box-shadow: 1px 1px 33px -11px inset; padding: 8px 25px; display: table-cell; 
+    /* support for firefox 3.5/3.6 and safari */
+    -moz-box-shadow: 1px 1px 33px -11px inset; -webkit-box-shadow: inset 1px 1px 33px -11px black; }
 
 /* links */
 a:link { color: #47F; text-decoration: none; }
@@ -575,7 +594,8 @@
 #moin-searchform input { font-size: 100%; vertical-align: middle;
             background-color: #F3F7FD; /* same as body bg col */
             border: 1px solid #A4B9DF; }
-#moin-searchform #moin-search-submit { display: none; }
+#moin-searchform #moin-search-submit { text-indent: -9000%; margin-left: -24px; width: 20px; height: 16px; background: url(../img/moin-search.png) center center no-repeat; background-size: 16px 16px; border: none; overflow: hidden; vertical-align: middle; cursor: pointer; }
+#moin-searchform #moin-search-query { padding-right: 20px; }
 #moin-searchform div { margin: 0; }
 #moin-searchform ul { border: 1px solid #A4B9DF; margin: 0; padding: 0; background-color: #F3F7FD; }
 #moin-searchform li { list-style:none; }
@@ -628,6 +648,25 @@
 .moin-itemviews form div { display: inline; margin: 0; }
 .moin-itemviews select { font-size: 100%; vertical-align: middle; }
 
+/* moin-header moin-subitem-navigation */
+.moin-subitem-navigation {  padding: 2px 4px; background: #C4D9FF; margin: 0; vertical-align: top;
+                font-size: 0.8em; width: 14em; overflow: hidden; display: table-cell; word-wrap: break-word; }
+.moin-subitem-navigation .list-header { margin: 2px 0; padding: 0; font-size: 1.1em; }
+.moin-subitem-navigation .sep { font-size: 1.2em; margin-left: 0px; }
+.moin-subitem-navigation ul { display: block; margin: 0; padding: 0 4px; }
+.moin-subitem-navigation li { padding: 0.5em 2px; line-height: 1em; list-style: none; }
+.moin-subitem-navigation li:hover { background: #4D7DA9; }
+.moin-subitem-navigation a { color: #0044B3; }
+.moin-subitem-navigation a:hover { text-decoration: underline; }
+.moin-subitem-navigation li ul { border-left: 1px dotted gray; display: none; }
+.moin-subitem-navigation .expander { background: url(../img/moin-expand.png) no-repeat center center; cursor: pointer;
+                height: 16px; overflow: hidden; vertical-align: middle; width: 16px; border: hidden; text-indent: -9000%; }
+.link-action,
+.transclude-action { border: hidden; text-indent: -9000%; padding: 0; margin: 0 -2px; vertical-align: middle;
+                height: 16px; width: 16px; overflow: hidden; cursor: pointer; }
+.moin-subitem-navigation .link-action { background: url(../img/moin-link.png) no-repeat center center; }
+.moin-subitem-navigation .transclude-action { background: url(../img/moin-transclusion.png) no-repeat center center; }
+
 /* moin-page moin-footer */
 #moin-footer { clear: both; margin: 0 0; }
 #moin-footer hr { margin: 0; background-color: #ccc; }
@@ -663,7 +702,7 @@
 h3:hover .permalink,
 h4:hover .permalink,
 h5:hover .permalink,
-h6:hover .permalink { display: inline; }
+h6:hover .permalink { display: inline; text-decoration: none; }
 
 } /* end of @media screen, projection */
 
Binary file MoinMoin/themes/modernized/static/img/moin-link.png has changed
Binary file MoinMoin/themes/modernized/static/img/moin-transclusion.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/translations/ru/LC_MESSAGES/messages.po	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,1305 @@
+
+msgid ""
+msgstr ""
+"Project-Id-Version: moin 2.0\n"
+"Report-Msgid-Bugs-To: Russian <moin-user@lists.sourceforge.net>\n"
+"POT-Creation-Date: 2011-07-10 21:27+0200\n"
+"PO-Revision-Date: 2011-12-04 17:55+0400\n"
+"Last-Translator: S Kinder <skinder0@gmail.com>\n"
+"Language-Team: Russian <moin-devel@lists.sourceforge.net>\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: MoinMoin/user.py:48
+#, python-format
+msgid ""
+"Invalid user name '%(name)s'.\n"
+"Name may contain any Unicode alpha numeric character, with optional one\n"
+"space between words. Group page name is not allowed."
+msgstr ""
+"Недопустимое имя пользователя «%(name)s».\n"
+"Имя может содержать буквы и цифры любого языка, между словами - не более "
+"одного пробела. Нельзя использовать имя группы."
+
+#: MoinMoin/user.py:54
+msgid "This user name already belongs to somebody else."
+msgstr "Это имя уже занято."
+
+#: MoinMoin/user.py:60
+#, python-format
+msgid "Password not acceptable: %(msg)s"
+msgstr "Пароль не принят: %(msg)s"
+
+#: MoinMoin/user.py:72
+msgid ""
+"Please provide your email address. If you lose your login information, "
+"you can get it by email."
+msgstr ""
+"Пожалуйста, укажите Ваш почтовый адрес. Если Вы потеряете данные для "
+"аутентификации (входа в вики), Вы сможете получить их по почте."
+
+#: MoinMoin/user.py:78
+msgid "This email already belongs to somebody else."
+msgstr "Этот почтовый адрес уже кем-то используется."
+
+#: MoinMoin/user.py:83
+msgid "This OpenID already belongs to somebody else."
+msgstr "Этот OpenID уже кем-то используется."
+
+#: MoinMoin/user.py:726
+msgid "<unknown>"
+msgstr "<неизвестно>"
+
+#: MoinMoin/user.py:784
+#, python-format
+msgid ""
+"Somebody has requested to email you a password recovery link.\n"
+"\n"
+"Please use the link below to change your password to a known value:\n"
+"\n"
+"%(link)s\n"
+"\n"
+"If you didn't forget your password, please ignore this email.\n"
+"\n"
+msgstr ""
+"Кто-то запросил отправку Вам ссылки для сброса пароля.\n"
+"\n"
+"Пожалуйста, пройдите по ссылке ниже, чтобы сменить ваш пароль:\n"
+"\n"
+"%(link)s\n"
+"\n"
+"Если Вы не забывали свой пароль, проигнорируйте это письмо.\n"
+"\n"
+
+#: MoinMoin/user.py:787
+#, python-format
+msgid "[%(sitename)s] Your wiki password recovery link"
+msgstr "[%(sitename)s] Ваша ссылка для восстановления пароля"
+
+#: MoinMoin/apps/admin/views.py:63
+#, python-format
+msgid "User profile of %(username)s: %(email)r"
+msgstr "Профиль пользователя %(username)s: %(email)r"
+
+#: MoinMoin/apps/admin/views.py:113
+#, python-format
+msgid "System items upgrade failed due to the following error: %(error)s."
+msgstr "Обновление системных объектов не удалось из-за следущей ошибки: %(error)s."
+
+#: MoinMoin/apps/admin/views.py:115
+msgid "System items have been upgraded successfully!"
+msgstr "Системные объекты успешно обновились!"
+
+#: MoinMoin/apps/admin/views.py:199
+msgid "Lexer description"
+msgstr "Описание лексера"
+
+#: MoinMoin/apps/admin/views.py:200
+msgid "Lexer names"
+msgstr "Имя лексера"
+
+#: MoinMoin/apps/admin/views.py:201
+msgid "File patterns"
+msgstr "Типы файлов"
+
+#: MoinMoin/apps/admin/views.py:202
+msgid "Mimetypes"
+msgstr "MIME-типы"
+
+#: MoinMoin/apps/admin/views.py:216
+msgid "InterWiki name"
+msgstr "Имя ИнтерВики"
+
+#: MoinMoin/apps/admin/views.py:217
+msgid "URL"
+msgstr "URL"
+
+#: MoinMoin/apps/admin/views.py:229 MoinMoin/templates/history.html:13
+msgid "Size"
+msgstr "Размер"
+
+#: MoinMoin/apps/admin/views.py:230
+msgid "Item name"
+msgstr "Имя объекта"
+
+#: MoinMoin/apps/admin/templates/admin/highlighterhelp.html:4
+#: MoinMoin/apps/admin/templates/admin/index.html:20
+msgid "Available Highlighters"
+msgstr "Доступные варианты подстветки синтаксиса"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:3
+msgid "Documentation"
+msgstr "Документация"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:5
+msgid "Documentation (local)"
+msgstr "Документация (локальная)"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:7
+msgid "Admin Menu"
+msgstr "Меню администрирования"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:9
+msgid "User Browser"
+msgstr "Просмотр пользователей"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:10
+#: MoinMoin/apps/admin/templates/admin/sysitems_upgrade.html:10
+msgid "Upgrade system items"
+msgstr "Обновить системные объекты"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:11
+msgid "Show Wiki Configuration"
+msgstr "Показать конфигурацию вики"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:12
+msgid "Show Wiki Configuration Help"
+msgstr "Показать помощь по конфигурации вики"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:14
+msgid "User Menu"
+msgstr "Пользовательское меню"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:16
+#: MoinMoin/apps/frontend/views.py:588
+msgid "Wanted Items"
+msgstr "Требуемые объекты"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:17
+#: MoinMoin/apps/frontend/views.py:636
+msgid "Orphaned Items"
+msgstr "Объекты-сироты"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:18
+#: MoinMoin/apps/admin/templates/admin/itemsize.html:4
+msgid "Item sizes (latest revision)"
+msgstr "Размеры объектов (последних версий)"
+
+#: MoinMoin/apps/admin/templates/admin/index.html:19
+#: MoinMoin/apps/admin/templates/admin/interwikihelp.html:4
+msgid "Known InterWiki names"
+msgstr "Известные имена ИнтерВики"
+
+#: MoinMoin/apps/admin/templates/admin/sysitems_upgrade.html:3
+msgid "System items upgrade"
+msgstr "Обновление системных объектов"
+
+#: MoinMoin/apps/admin/templates/admin/sysitems_upgrade.html:5
+msgid ""
+"You can upgrade your system items by uploading an xml file with new items"
+" below."
+msgstr ""
+"Вы можете обновить системные элементы, загрузив XML-файл с новыми "
+"объектами ниже."
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:5
+msgid "User name"
+msgstr "Имя пользователя"
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:6
+msgid "Member of Groups"
+msgstr "Член групп"
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:7
+msgid "Email address"
+msgstr "Адрес электронной почты"
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:8
+#: MoinMoin/templates/history.html:18
+msgid "Actions"
+msgstr "Действия"
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:12
+msgid "disabled"
+msgstr "запрещен"
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:23
+msgid "Enable user"
+msgstr "Разрешить использование учетной записи"
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:23
+msgid "Disable user"
+msgstr "Запретить использование учетной записи"
+
+#: MoinMoin/apps/admin/templates/admin/userbrowser.html:28
+msgid "Mail account data"
+msgstr "Отправить мне почтой информацию по учетной записи"
+
+#: MoinMoin/apps/admin/templates/admin/wikiconfig.html:3
+msgid "Wiki configuration"
+msgstr "Конфигурация вики"
+
+#: MoinMoin/apps/admin/templates/admin/wikiconfig.html:5
+msgid ""
+"This table shows all settings in this wiki that do not have default "
+"values. Settings that the configuration system doesn't know about are "
+"shown in italic, those may be due to third-party extensions needing "
+"configuration or settings that were removed from Moin."
+msgstr ""
+"Эта таблица содержит все настройки вики, для которых нет значений по "
+"умолчанию. ''Курсивом'' отмечены настройки, неизвестные системе. "
+"Возможно, они относятся к расширениям сторонних разработчиков или к "
+"предыдущим версиям МойнМойн."
+
+#: MoinMoin/apps/admin/templates/admin/wikiconfig.html:14
+#: MoinMoin/apps/admin/templates/admin/wikiconfighelp.html:12
+msgid "Variable name"
+msgstr "Имя переменной"
+
+#: MoinMoin/apps/admin/templates/admin/wikiconfig.html:15
+msgid "Setting"
+msgstr "Настройка"
+
+#: MoinMoin/apps/admin/templates/admin/wikiconfighelp.html:3
+msgid "WikiConfig Help"
+msgstr "Помощь по WikiConfig"
+
+#: MoinMoin/apps/admin/templates/admin/wikiconfighelp.html:13
+msgid "Default"
+msgstr "По умолчанию"
+
+#: MoinMoin/apps/admin/templates/admin/wikiconfighelp.html:14
+msgid "Description"
+msgstr "Описание"
+
+#: MoinMoin/apps/feed/views.py:63
+msgid "MoinMoin feels unhappy."
+msgstr "МойнМойн плохо себя чувствует."
+
+#: MoinMoin/apps/frontend/views.py:114
+msgid "Search query too short."
+msgstr "Слишком короткий поисковой запрос."
+
+#: MoinMoin/apps/frontend/views.py:125
+msgid "Search Query"
+msgstr "Поиск"
+
+#: MoinMoin/apps/frontend/views.py:126
+msgid "Search"
+msgstr "Искать"
+
+#: MoinMoin/apps/frontend/views.py:308
+#: MoinMoin/templates/global_history.html:20 MoinMoin/templates/history.html:17
+msgid "Comment"
+msgstr "Комментарий"
+
+#: MoinMoin/apps/frontend/views.py:308
+msgid "Comment about your change"
+msgstr "Описание правки"
+
+#: MoinMoin/apps/frontend/views.py:309
+msgid "OK"
+msgstr "OK"
+
+#: MoinMoin/apps/frontend/views.py:312
+msgid "Target"
+msgstr "Цель"
+
+#: MoinMoin/apps/frontend/views.py:312
+msgid "The name of the target item"
+msgstr "Имя объекта"
+
+#: MoinMoin/apps/frontend/views.py:531
+msgid "Refers Here"
+msgstr "Ссылки сюда"
+
+#: MoinMoin/apps/frontend/views.py:672 MoinMoin/apps/frontend/views.py:691
+#, python-format
+msgid "You must login to use this action: %(action)s."
+msgstr "Вам необходимо войти, чтобы использовать действие %(action)s."
+
+#: MoinMoin/apps/frontend/views.py:675
+msgid "A quicklink to this page could not be added for you."
+msgstr "Закладка на эту страницу не может быть добавлена для Вас."
+
+#: MoinMoin/apps/frontend/views.py:678
+msgid "Your quicklink to this page could not be removed."
+msgstr "Ваша закладка на эту страницу не может быть удалена."
+
+#: MoinMoin/apps/frontend/views.py:693
+msgid "You are not allowed to subscribe to an item you may not read."
+msgstr "Вы не можете подписаться на объект, который Вам нельзя читать."
+
+#: MoinMoin/apps/frontend/views.py:697
+msgid "Can't remove regular expression subscription!"
+msgstr "Невозможно удалить подписку по регулярному выражению."
+
+#: MoinMoin/apps/frontend/views.py:698
+msgid "Edit the subscription regular expressions in your settings."
+msgstr "Отредактируйте регулярные выражения подписки в ваших настройках."
+
+#: MoinMoin/apps/frontend/views.py:702
+msgid "You could not get subscribed to this item."
+msgstr "Не удалось подписаться на этот объект."
+
+#: MoinMoin/apps/frontend/views.py:711 MoinMoin/apps/frontend/views.py:904
+#: MoinMoin/apps/frontend/views.py:1041
+msgid "The passwords do not match."
+msgstr "Пароли не совпадают."
+
+#: MoinMoin/apps/frontend/views.py:727 MoinMoin/apps/frontend/views.py:743
+#: MoinMoin/apps/frontend/views.py:863 MoinMoin/apps/frontend/views.py:922
+#: MoinMoin/apps/frontend/views.py:989 MoinMoin/apps/frontend/views.py:1107
+#: MoinMoin/templates/global_history.html:16 MoinMoin/templates/history.html:10
+msgid "Name"
+msgstr "Имя"
+
+#: MoinMoin/apps/frontend/views.py:727 MoinMoin/apps/frontend/views.py:743
+#: MoinMoin/apps/frontend/views.py:1107
+msgid "The login name you want to use"
+msgstr "Имя, которое Вы хотите использовать при входе"
+
+#: MoinMoin/apps/frontend/views.py:728 MoinMoin/apps/frontend/views.py:729
+#: MoinMoin/apps/frontend/views.py:744 MoinMoin/apps/frontend/views.py:745
+#: MoinMoin/apps/frontend/views.py:990
+msgid "Password"
+msgstr "Пароль"
+
+#: MoinMoin/apps/frontend/views.py:728 MoinMoin/apps/frontend/views.py:744
+#: MoinMoin/apps/frontend/views.py:924 MoinMoin/apps/frontend/views.py:1065
+msgid "The login password you want to use"
+msgstr "Пароль, который Вы хотите использовать при входе"
+
+#: MoinMoin/apps/frontend/views.py:729 MoinMoin/apps/frontend/views.py:745
+#: MoinMoin/apps/frontend/views.py:925 MoinMoin/apps/frontend/views.py:1066
+msgid "Repeat the same password"
+msgstr "Повторите тот же пароль"
+
+#: MoinMoin/apps/frontend/views.py:730 MoinMoin/apps/frontend/views.py:747
+#: MoinMoin/apps/frontend/views.py:864 MoinMoin/apps/frontend/views.py:1073
+msgid "E-Mail"
+msgstr "Адрес электронной почты"
+
+#: MoinMoin/apps/frontend/views.py:730 MoinMoin/apps/frontend/views.py:747
+#: MoinMoin/apps/frontend/views.py:864 MoinMoin/apps/frontend/views.py:1073
+msgid "Your E-Mail address"
+msgstr "Адрес Вашей электронной почты"
+
+#: MoinMoin/apps/frontend/views.py:731 MoinMoin/apps/frontend/views.py:748
+#: MoinMoin/apps/frontend/views.py:991 MoinMoin/apps/frontend/views.py:1109
+msgid "OpenID"
+msgstr "OpenID"
+
+#: MoinMoin/apps/frontend/views.py:731 MoinMoin/apps/frontend/views.py:748
+#: MoinMoin/apps/frontend/views.py:1109
+msgid "Your OpenID address"
+msgstr "Ваш OpenID"
+
+#: MoinMoin/apps/frontend/views.py:732
+#: MoinMoin/templates/openid_register.html:21
+msgid "Register"
+msgstr "Зарегистрироваться"
+
+#: MoinMoin/apps/frontend/views.py:810 MoinMoin/apps/frontend/views.py:836
+msgid "Account created, please log in now."
+msgstr "Учетная запись создана, теперь Вы можете войти в систему."
+
+#: MoinMoin/apps/frontend/views.py:848
+msgid "Your user name or your email address is needed."
+msgstr "Необходимо Ваше имя пользователя или адрес электронной почты."
+
+#: MoinMoin/apps/frontend/views.py:863 MoinMoin/apps/frontend/views.py:922
+msgid "Your login name"
+msgstr "Ваше имя пользователя"
+
+#: MoinMoin/apps/frontend/views.py:865
+msgid "Recover password"
+msgstr "Восстановить пароль"
+
+#: MoinMoin/apps/frontend/views.py:894
+msgid "If this account exists, you will be notified."
+msgstr ""
+"Если данная учетная запись существует, то по связанному с ней почтовому "
+"адресу будет выслано оповещение."
+
+#: MoinMoin/apps/frontend/views.py:905 MoinMoin/apps/frontend/views.py:1043
+msgid "New password is unacceptable, encoding trouble."
+msgstr "Новый пароль не может быть принят, проблема с преобразованием кодировок."
+
+#: MoinMoin/apps/frontend/views.py:923
+msgid "Recovery token"
+msgstr "Строка сброса пароля"
+
+#: MoinMoin/apps/frontend/views.py:923
+msgid "The recovery token that has been sent to you"
+msgstr "Строка сброса пароля, которая была отправлена Вам"
+
+#: MoinMoin/apps/frontend/views.py:924 MoinMoin/apps/frontend/views.py:1065
+msgid "New password"
+msgstr "Новый пароль"
+
+#: MoinMoin/apps/frontend/views.py:925 MoinMoin/apps/frontend/views.py:1066
+msgid "New password (repeat)"
+msgstr "Повторите новый пароль"
+
+#: MoinMoin/apps/frontend/views.py:926 MoinMoin/apps/frontend/views.py:1067
+#: MoinMoin/templates/usersettings.html:9
+msgid "Change password"
+msgstr "Поменять пароль"
+
+#: MoinMoin/apps/frontend/views.py:947
+msgid "Your password has been changed, you can log in now."
+msgstr "Ваш пароль был изменен, теперь Вы можете войти в систему."
+
+#: MoinMoin/apps/frontend/views.py:949
+msgid "Your token is invalid!"
+msgstr "Строка сброса неверна."
+
+#: MoinMoin/apps/frontend/views.py:961
+msgid "Either your username or password was invalid."
+msgstr "Неверное имя пользователя или пароль."
+
+#: MoinMoin/apps/frontend/views.py:962
+msgid "Failed to authenticate with this OpenID."
+msgstr "Ошибка при аутентификации с этим OpenID."
+
+#: MoinMoin/apps/frontend/views.py:1031
+msgid "You are now logged out."
+msgstr "Вы вышли."
+
+#: MoinMoin/apps/frontend/views.py:1042
+msgid "The current password was wrong."
+msgstr "Текущий пароль указан неверно."
+
+#: MoinMoin/apps/frontend/views.py:1064
+msgid "Current Password"
+msgstr "Текущий пароль"
+
+#: MoinMoin/apps/frontend/views.py:1064
+msgid "Your current login password"
+msgstr "Ваш текущий пароль"
+
+#: MoinMoin/apps/frontend/views.py:1074 MoinMoin/apps/frontend/views.py:1080
+#: MoinMoin/apps/frontend/views.py:1095 MoinMoin/apps/frontend/views.py:1118
+#: MoinMoin/apps/frontend/views.py:1128
+msgid "Save"
+msgstr "Сохранить"
+
+#: MoinMoin/apps/frontend/views.py:1091
+msgid "Publish my email (not my wiki homepage) in author info"
+msgstr ""
+"Показывать мой почтовый адрес (вместо домашней страницы) в информации об "
+"авторе"
+
+#: MoinMoin/apps/frontend/views.py:1092
+msgid "Open editor on double click"
+msgstr "Открывать редактор по двойному щелчку"
+
+#: MoinMoin/apps/frontend/views.py:1093
+msgid "Show comment sections"
+msgstr "Показывать комментарии по умолчанию при загрузке страницы"
+
+#: MoinMoin/apps/frontend/views.py:1094
+msgid "Disable this account forever"
+msgstr "Заблокировать эту учетную запись навсегда"
+
+#: MoinMoin/apps/frontend/views.py:1108
+msgid "Alias-Name"
+msgstr "Псевдоним"
+
+#: MoinMoin/apps/frontend/views.py:1108
+msgid "Your alias name (informational)"
+msgstr "Ваш псевдоним"
+
+#: MoinMoin/apps/frontend/views.py:1112
+msgid "Timezone"
+msgstr "Часовой пояс"
+
+#: MoinMoin/apps/frontend/views.py:1117
+msgid "Locale"
+msgstr "Язык"
+
+#: MoinMoin/apps/frontend/views.py:1125
+msgid "Theme name"
+msgstr "Тема оформления"
+
+#: MoinMoin/apps/frontend/views.py:1126
+msgid "User CSS URL"
+msgstr "URL пользовательского CSS"
+
+#: MoinMoin/apps/frontend/views.py:1126
+msgid "Give the URL of your custom CSS (optional)"
+msgstr "Ссылка на URL пользовательского CSS (опционально)"
+
+#: MoinMoin/apps/frontend/views.py:1127
+msgid "Editor size"
+msgstr "Размер окна редактирования"
+
+#: MoinMoin/apps/frontend/views.py:1127
+msgid "Editor textarea height (0=auto)"
+msgstr "Высота текстового поля (0=по умолчанию)"
+
+#: MoinMoin/apps/frontend/views.py:1156
+msgid "Your password has been changed."
+msgstr "Ваш пароль был изменен."
+
+#: MoinMoin/apps/frontend/views.py:1161
+msgid "This openid is already in use."
+msgstr "Этот OpenID уже кем-то используется."
+
+#: MoinMoin/apps/frontend/views.py:1165
+msgid "This username is already in use."
+msgstr "Это имя пользователя уже кем-то используется"
+
+#: MoinMoin/apps/frontend/views.py:1171
+msgid "This email is already in use"
+msgstr "Этот адрес электронной почты уже кем-то используется"
+
+#: MoinMoin/apps/frontend/views.py:1209
+msgid "You must log in to use bookmarks."
+msgstr "Вы должны войти, чтобы пользоваться закладками."
+
+#: MoinMoin/apps/frontend/views.py:1362 MoinMoin/config/default.py:374
+msgid "Items with similar names"
+msgstr "Объекты с похожими именами"
+
+#: MoinMoin/apps/frontend/views.py:1563
+msgid "All tags in this wiki"
+msgstr "Все теги в этой вики"
+
+#: MoinMoin/apps/frontend/views.py:1575
+#, python-format
+msgid "Items tagged with %(tag)s"
+msgstr "Объекты с тегом %(tag)s"
+
+#: MoinMoin/auth/__init__.py:242 MoinMoin/auth/ldap_login.py:129
+msgid "Missing password. Please enter user name and password."
+msgstr "Отсутствует пароль. Пожалуйста, введите имя пользователя и пароль."
+
+#: MoinMoin/auth/__init__.py:250 MoinMoin/auth/ldap_login.py:199
+#: MoinMoin/auth/ldap_login.py:245
+msgid "Invalid username or password."
+msgstr "Неправильное имя пользователя или пароль."
+
+#: MoinMoin/auth/__init__.py:254
+#, python-format
+msgid ""
+"If you do not have an account, <a href=\"%(register_url)s\">you can "
+"create one now</a>. "
+msgstr ""
+"Если у Вас нет учетной записи, <a href=\"%(register_url)s\">Вы можете "
+"создать ее сейчас</a>. "
+
+#: MoinMoin/auth/__init__.py:256
+#, python-format
+msgid "<a href=\"%(recover_url)s\">Forgot your password?</a>"
+msgstr "<a href=\"%(recover_url)s\">Забыли пароль?</a>"
+
+#: MoinMoin/auth/http.py:62
+msgid "Please log in first."
+msgstr "Пожалуйста, предварительно аутентифицируйтесь на вики."
+
+#: MoinMoin/auth/ldap_login.py:259
+#, python-format
+msgid "LDAP server %(server)s failed."
+msgstr "LDAP-сервер %(server)s недоступен."
+
+#: MoinMoin/auth/openidrp.py:63
+msgid "OpenID Error"
+msgstr "Ошибка OpenID"
+
+#: MoinMoin/auth/openidrp.py:70
+msgid "OpenID verification cancelled."
+msgstr "Проверка OpenID прервана."
+
+#: MoinMoin/auth/openidrp.py:104
+msgid "This OpenID provider is not trusted."
+msgstr "Этот провайдер OpenID не является доверенным."
+
+#: MoinMoin/auth/openidrp.py:109
+msgid "OpenID failure."
+msgstr "Ошибка OpenID."
+
+#: MoinMoin/auth/openidrp.py:143
+msgid "Failed to resolve OpenID."
+msgstr "Ошибка при разрешении OpenID."
+
+#: MoinMoin/auth/openidrp.py:145
+msgid "OpenID discovery failure, not a valid OpenID."
+msgstr "Ошибка нахождения OpenID, OpenID не верный."
+
+#: MoinMoin/auth/openidrp.py:149
+msgid "No OpenID service at this URL."
+msgstr "По заданному адресу не найдено сервиса OpenID."
+
+#: MoinMoin/config/default.py:250
+msgid "Password is too short."
+msgstr "Пароль слишком короткий."
+
+#: MoinMoin/config/default.py:252
+msgid "Password has not enough different characters."
+msgstr ""
+"Слишком простой пароль — в пароле должно присутствовать больше разных "
+"символов."
+
+#: MoinMoin/config/default.py:258
+msgid ""
+"Password is too easy to guess (password contains name or name contains "
+"password)."
+msgstr ""
+"Слишком простой пароль — в пароле присутствует имя пользователя, либо "
+"пароль содержится в имени."
+
+#: MoinMoin/config/default.py:267
+msgid "Password is too easy to guess (keyboard sequence)."
+msgstr "Слишком простой пароль — клавиатурная последовательность."
+
+#: MoinMoin/config/default.py:327
+msgid "Home"
+msgstr "Домой"
+
+#: MoinMoin/config/default.py:327
+msgid "Home Page"
+msgstr "Домашняя страница"
+
+#: MoinMoin/config/default.py:328 MoinMoin/config/default.py:356
+msgid "History"
+msgstr "История"
+
+#: MoinMoin/config/default.py:328 MoinMoin/templates/global_history.html:10
+msgid "Global History"
+msgstr "Глобальная история"
+
+#: MoinMoin/config/default.py:329 MoinMoin/config/default.py:361
+#: MoinMoin/config/default.py:362
+msgid "Index"
+msgstr "Указатель"
+
+#: MoinMoin/config/default.py:329 MoinMoin/templates/global_index.html:4
+msgid "Global Index"
+msgstr "Глобальный указатель"
+
+#: MoinMoin/config/default.py:330
+msgid "Tags"
+msgstr "Теги"
+
+#: MoinMoin/config/default.py:330
+msgid "Global Tags Index"
+msgstr "Указатель глобальных тегов"
+
+#: MoinMoin/config/default.py:331
+msgid "More"
+msgstr "Прочее"
+
+#: MoinMoin/config/default.py:331
+msgid "Administration & Docs"
+msgstr "Администрирование и документация"
+
+#: MoinMoin/config/default.py:354
+msgid "Show"
+msgstr "Показать"
+
+#: MoinMoin/config/default.py:355
+msgid "Download"
+msgstr "Загрузить"
+
+#: MoinMoin/config/default.py:356
+msgid "Revision History"
+msgstr "История изменений"
+
+#: MoinMoin/config/default.py:359 MoinMoin/templates/show.html:9
+msgid "Modify"
+msgstr "Изменить"
+
+#: MoinMoin/config/default.py:359
+msgid "Edit or Upload"
+msgstr "Править или загрузить"
+
+#: MoinMoin/config/default.py:361 MoinMoin/config/default.py:362
+msgid "List sub-items"
+msgstr "Список дочерних объектов"
+
+#: MoinMoin/config/default.py:363
+msgid "Comments"
+msgstr "Комментарии"
+
+#: MoinMoin/config/default.py:363
+msgid "Switch showing comments on or off"
+msgstr "Показать или скрыть комментарии"
+
+#: MoinMoin/config/default.py:364
+msgid "Highlight"
+msgstr "Подсветка"
+
+#: MoinMoin/config/default.py:364
+msgid "Show with Syntax-Highlighting"
+msgstr "Показать с подстветкой синтаксиса"
+
+#: MoinMoin/config/default.py:365
+msgid "Meta"
+msgstr "Мета"
+
+#: MoinMoin/config/default.py:365
+msgid "Display Metadata"
+msgstr "Показать метаданные"
+
+#: MoinMoin/config/default.py:366
+msgid "Create or remove a navigation link to this item"
+msgstr "Добавить или удалить ссылку на объект в панель навигации"
+
+#: MoinMoin/config/default.py:367
+msgid "Switch notifications about item changes on or off"
+msgstr "Выключить или включить уведомления об изменениях объекта"
+
+#: MoinMoin/config/default.py:368
+msgid "Copy"
+msgstr "Копировать"
+
+#: MoinMoin/config/default.py:368
+msgid "Create a copy of this item"
+msgstr "Создать копию этого объекта"
+
+#: MoinMoin/config/default.py:369
+msgid "Rename"
+msgstr "Переименовать"
+
+#: MoinMoin/config/default.py:369
+msgid "Rename this item"
+msgstr "Переименовать этот объект"
+
+#: MoinMoin/config/default.py:370
+msgid "Delete"
+msgstr "Удалить"
+
+#: MoinMoin/config/default.py:370
+msgid "Move this item to the trash"
+msgstr "Переместить объект в корзину"
+
+#: MoinMoin/config/default.py:371
+msgid "Destroy"
+msgstr "Уничтожить"
+
+#: MoinMoin/config/default.py:371
+msgid "Completely destroy this item"
+msgstr "Полностью уничтожить этот объект"
+
+#: MoinMoin/config/default.py:372
+msgid "Referrers"
+msgstr "Ссылки"
+
+#: MoinMoin/config/default.py:372
+msgid "What refers here?"
+msgstr "Ссылки сюда"
+
+#: MoinMoin/config/default.py:373
+msgid "Site Map"
+msgstr "Карта сайта"
+
+#: MoinMoin/config/default.py:373
+msgid "Local Site Map of this item"
+msgstr "Карта окрестностей этого объекта"
+
+#: MoinMoin/config/default.py:374
+msgid "Similar"
+msgstr "Похожее"
+
+#: MoinMoin/converter/html_out.py:591
+msgid "Contents"
+msgstr "Содержимое"
+
+#: MoinMoin/converter/macro.py:84
+#, python-format
+msgid "<<%(macro_name)s: execution failed [%(error_msg)s] (see also the log)>>"
+msgstr ""
+"<<%(macro_name)s: выполнение завершилось с ошибкой «%(error_msg)s» (см. "
+"диагностику на сервере)>>"
+
+#: MoinMoin/converter/moinwiki_in.py:1032
+msgid "Error:"
+msgstr "Ошибка"
+
+#: MoinMoin/converter/moinwiki_in.py:1033
+msgid "is invalid within"
+msgstr "неверно в пределах"
+
+#: MoinMoin/converter/nonexistent_in.py:30
+#, python-format
+msgid "%(item_name)s does not exist. Create it?"
+msgstr "Объект %(item_name)s не существет. Создать его?"
+
+#: MoinMoin/items/__init__.py:654
+msgid "Invalid JSON."
+msgstr "Неправильный JSON."
+
+#: MoinMoin/items/__init__.py:700 MoinMoin/items/__init__.py:1156
+#: MoinMoin/items/__init__.py:1353 MoinMoin/items/__init__.py:1445
+#: MoinMoin/items/__init__.py:1533
+msgid "MetaData (JSON)"
+msgstr "Метаданные (JSON)"
+
+#: MoinMoin/items/__init__.py:701 MoinMoin/items/__init__.py:1158
+#: MoinMoin/items/__init__.py:1354 MoinMoin/items/__init__.py:1446
+#: MoinMoin/items/__init__.py:1534
+msgid "Upload file:"
+msgstr "Загрузить файл:"
+
+#: MoinMoin/items/__init__.py:735
+msgid ""
+"The items have the same data hash code (that means they very likely have "
+"the same data)."
+msgstr ""
+"У объектов одинаковый хеш (скорее всего, в них содержится одинаковая "
+"информация)."
+
+#: MoinMoin/items/__init__.py:737
+msgid "The items have different data."
+msgstr "В объектах разные данные."
+
+#: MoinMoin/items/__init__.py:744
+#, python-format
+msgid "Impossible to convert the data to the contenttype: %(contenttype)s"
+msgstr "Невозможно перевести данные в тип %(contenttype)s"
+
+#: MoinMoin/items/__init__.py:1157
+msgid "Type your text here"
+msgstr "Введите Ваш текст сюда"
+
+#: MoinMoin/items/__init__.py:1384 MoinMoin/items/__init__.py:1481
+#, python-format
+msgid "Edit drawing %(filename)s (opens in new window)"
+msgstr "Изменить рисунок «%(filename)s» (откроется в новом окне)"
+
+#: MoinMoin/items/__init__.py:1399 MoinMoin/items/__init__.py:1498
+#, python-format
+msgid "Clickable drawing: %(filename)s"
+msgstr "Рисунок, содержащий активные зоны: «%(filename)s»"
+
+#: MoinMoin/mail/sendmail.py:86
+msgid "No recipients, nothing to do"
+msgstr "Получатели не указаны, никаких действий не требуется."
+
+#: MoinMoin/mail/sendmail.py:162
+#, python-format
+msgid "Connection to mailserver '%(server)s' failed: %(reason)s"
+msgstr "Соединиться с почтовым сервером «%(server)s» не удалось: «%(reason)s»"
+
+#: MoinMoin/mail/sendmail.py:176
+msgid "Mail not sent"
+msgstr "Почта не отправлена."
+
+#: MoinMoin/mail/sendmail.py:179
+msgid "Mail sent successfully"
+msgstr "Почта отправлена."
+
+#: MoinMoin/security/textcha.py:159
+msgid "The entered TextCha was incorrect."
+msgstr "Введенная текстовая капча была неверной."
+
+#: MoinMoin/security/textcha.py:160
+msgid "The TextCha question is invalid or has expired. Please try again."
+msgstr "Текстовая капча неверна или просрочена. Попробуйте снова."
+
+#: MoinMoin/security/textcha.py:179
+msgid "TextCha"
+msgstr "Текстовая капча"
+
+#: MoinMoin/storage/error.py:42
+msgid "Permission denied!"
+msgstr "Доступ запрещен!"
+
+#: MoinMoin/storage/error.py:44
+msgid "You"
+msgstr "Вы"
+
+#: MoinMoin/storage/error.py:46
+#, python-format
+msgid "%(username)s may not %(priv)s '%(item)s'."
+msgstr "%(username)s не может %(priv)s «%(item)s»."
+
+#: MoinMoin/templates/copy.html:4
+#, python-format
+msgid "Copy '%(item_name)s'"
+msgstr "Копировать «%(item_name)s»"
+
+#: MoinMoin/templates/delete.html:4
+#, python-format
+msgid "Delete '%(item_name)s'"
+msgstr "Удалить «%(item_name)s»"
+
+#: MoinMoin/templates/destroy.html:5
+#, python-format
+msgid "DESTROY COMPLETE item '%(item_name)s'"
+msgstr "УНИЧТОЖИТЬ ВЕСЬ объект «%(item_name)s»"
+
+#: MoinMoin/templates/destroy.html:17
+#, python-format
+msgid "DESTROY REVISION '%(item_name)s' (rev %(rev_no)d)"
+msgstr "УНИЧТОЖИТЬ ВЕРСИЮ «%(item_name)s» (вер. %(rev_no)d)"
+
+#: MoinMoin/templates/diff.html:4
+#, python-format
+msgid "Item: %(item_name)s (Diff between revision %(oldrevno)s and %(newrevno)s)"
+msgstr "Объект: %(item_name)s (Разница между версиями %(oldrevno)s и %(newrevno)s)"
+
+#: MoinMoin/templates/diff_text.html:28
+msgid "No older revision"
+msgstr "Нет более старых версий"
+
+#: MoinMoin/templates/diff_text.html:32
+msgid "Use older revision"
+msgstr "Использовать более старую версию"
+
+#: MoinMoin/templates/diff_text.html:34
+#, python-format
+msgid "Revision %(revno)d as of %(date_time)s"
+msgstr "Версия: %(revno)d на момент %(date_time)s"
+
+#: MoinMoin/templates/diff_text.html:37
+msgid "No newer revision"
+msgstr "Нет более новых версий"
+
+#: MoinMoin/templates/diff_text.html:41
+msgid "Use newer revision"
+msgstr "Использовать более новую версию"
+
+#: MoinMoin/templates/diff_text.html:75
+msgid "Deletions are marked like this."
+msgstr "Удаления помечены так."
+
+#: MoinMoin/templates/diff_text.html:76
+msgid "Additions are marked like this."
+msgstr "Добавления помечены так."
+
+#: MoinMoin/templates/diff_text.html:80 MoinMoin/templates/diff_text.html:81
+msgid "Line"
+msgstr "Строка"
+
+#: MoinMoin/templates/global_history.html:14 MoinMoin/templates/history.html:12
+msgid "Timestamp"
+msgstr "Дата"
+
+#: MoinMoin/templates/global_history.html:15
+msgid "Action"
+msgstr "Действие"
+
+#: MoinMoin/templates/global_history.html:17 MoinMoin/templates/history.html:11
+msgid "Rev."
+msgstr "Вер."
+
+#: MoinMoin/templates/global_history.html:18 MoinMoin/templates/history.html:16
+msgid "Content-Type"
+msgstr "Content-Type"
+
+#: MoinMoin/templates/global_history.html:19 MoinMoin/templates/history.html:15
+msgid "Editor"
+msgstr "Редактор"
+
+#: MoinMoin/templates/highlight.html:4
+#, python-format
+msgid "Syntax highlighting for Item: %(name)s"
+msgstr "Подсветка синтаксиса для объекта %(name)s"
+
+#: MoinMoin/templates/history.html:5
+#, python-format
+msgid "History of '%(item_name)s'"
+msgstr "История «%(item_name)s»"
+
+#: MoinMoin/templates/history.html:35
+msgid "show"
+msgstr "показать"
+
+#: MoinMoin/templates/history.html:36
+msgid "meta"
+msgstr "мета"
+
+#: MoinMoin/templates/history.html:37
+msgid "download"
+msgstr "загрузить"
+
+#: MoinMoin/templates/history.html:38
+msgid "highlight"
+msgstr "подсветка"
+
+#: MoinMoin/templates/history.html:39
+msgid "revert"
+msgstr "возвратить"
+
+#: MoinMoin/templates/history.html:40
+msgid "destroy"
+msgstr "уничтожить"
+
+#: MoinMoin/templates/index.html:4 MoinMoin/templates/index2.html:9
+#, python-format
+msgid "Index of subitems of '%(item_name)s'"
+msgstr "Подобъекты объекта «%(item_name)s»"
+
+#: MoinMoin/templates/itemviews.html:30
+msgid "Remove Link"
+msgstr "Убрать из закладок"
+
+#: MoinMoin/templates/itemviews.html:32
+msgid "Add Link"
+msgstr "Добавить в закладки"
+
+#: MoinMoin/templates/itemviews.html:41
+msgid "Unsubscribe"
+msgstr "Отписаться"
+
+#: MoinMoin/templates/itemviews.html:43
+msgid "Subscribe"
+msgstr "Подписаться"
+
+#: MoinMoin/templates/layout.html:46 MoinMoin/templates/usersettings.html:6
+msgid "Settings"
+msgstr "Настройки"
+
+#: MoinMoin/templates/layout.html:52
+msgid "Logout"
+msgstr "Выйти"
+
+#: MoinMoin/templates/layout.html:58
+msgid "Login"
+msgstr "Войти"
+
+#: MoinMoin/templates/login.html:9
+msgid "Moin login"
+msgstr "Аутентификация на вики"
+
+#: MoinMoin/templates/login.html:21 MoinMoin/templates/login.html:35
+msgid "Log in"
+msgstr "Войти"
+
+#: MoinMoin/templates/login.html:28
+msgid "OpenID login"
+msgstr "Вход по OpenID"
+
+#: MoinMoin/templates/lostpass.html:5
+msgid "Lost Password"
+msgstr "Забытый пароль"
+
+#: MoinMoin/templates/lostpass.html:7
+msgid "Please note that you only need to fill out one form field."
+msgstr "Обратите внимание: вам необходимо заполнить только одно поле формы."
+
+#: MoinMoin/templates/meta.html:9
+#, python-format
+msgid "Metadata of '%(item_name)s'"
+msgstr "Метаданные «%(item_name)s»"
+
+#: MoinMoin/templates/meta.html:10
+msgid "Revision"
+msgstr "Версия"
+
+#: MoinMoin/templates/modify_anywikidraw.html:18
+#: MoinMoin/templates/modify_twikidraw.html:13
+msgid "NOTE:"
+msgstr "Обратите внимание:"
+
+#: MoinMoin/templates/modify_anywikidraw.html:18
+#: MoinMoin/templates/modify_twikidraw.html:13
+msgid "You need a Java enabled browser to edit the drawing."
+msgstr ""
+"Для изменения изображения в Вашем браузере должна быть включена поддержка"
+" Java."
+
+#: MoinMoin/templates/modify_applet.html:4
+#, python-format
+msgid "Modifying %(item_name)s"
+msgstr "Изменение %(item_name)s"
+
+#: MoinMoin/templates/modify_show_template_selection.html:3
+#: MoinMoin/templates/modify_show_type_selection.html:3
+msgid "Create new item?"
+msgstr "Создать новый объект?"
+
+#: MoinMoin/templates/modify_show_template_selection.html:5
+#, python-format
+msgid ""
+"You can either <a href='%(modifyhref)s'>create the item from scratch</a> "
+"or select a template."
+msgstr ""
+"Вы можете либо  <a href='%(modifyhref)s'>создать объект с нуля</a>, либо "
+"выбрать шаблон."
+
+#: MoinMoin/templates/modify_show_template_selection.html:8
+msgid "Available template items"
+msgstr "Доступные шаблоны"
+
+#: MoinMoin/templates/modify_show_type_selection.html:5
+msgid ""
+"This item does not exist (yet), but you can try creating it now. Please "
+"select the type of the item you want to create."
+msgstr ""
+"Этот объект (еще) не существует, но Вы можете создать его. Пожалуйста, "
+"выберите тип желаемого объекта."
+
+#: MoinMoin/templates/openid_register.html:5 MoinMoin/templates/register.html:5
+msgid "Create Account"
+msgstr "Создать уетную запись"
+
+#: MoinMoin/templates/recoverpass.html:5
+msgid "Recover Password"
+msgstr "Восстановить пароль"
+
+#: MoinMoin/templates/rename.html:4
+#, python-format
+msgid "Rename '%(item_name)s'"
+msgstr "Переименовать «%(item_name)s»"
+
+#: MoinMoin/templates/revert.html:4
+#, python-format
+msgid "Revert '%(item_name)s' (rev %(rev_no)d)"
+msgstr "Вернуть «%(item_name)s» (вер. %(rev_no)d)"
+
+#: MoinMoin/templates/show.html:23
+msgid "older"
+msgstr "предыдущая версия"
+
+#: MoinMoin/templates/show.html:25
+msgid "no older revision"
+msgstr "нет более старых версий"
+
+#: MoinMoin/templates/show.html:31
+msgid "newer"
+msgstr "следущая версия"
+
+#: MoinMoin/templates/show.html:33
+msgid "no newer revision"
+msgstr "нет более новых версий"
+
+#: MoinMoin/templates/show.html:47
+msgid "This item exists, but it has no revisions."
+msgstr "Этот объект существует, но не имеет версий."
+
+#: MoinMoin/templates/show.html:72
+msgid "modified"
+msgstr "изменен"
+
+#: MoinMoin/templates/show.html:73
+msgid "by"
+msgstr "пользователем"
+
+#: MoinMoin/templates/show.html:74
+msgid "tagged"
+msgstr "теги:"
+
+#: MoinMoin/templates/sitemap.html:3
+#, python-format
+msgid "SiteMap of %(item_name)s"
+msgstr "КартаОкрестностей для %(item_name)s"
+
+#: MoinMoin/templates/usersettings.html:8
+#: MoinMoin/templates/usersettings.html:17
+msgid "Personal Settings"
+msgstr "Личные настройки"
+
+#: MoinMoin/templates/usersettings.html:10
+#: MoinMoin/templates/usersettings.html:47
+msgid "Notification Settings"
+msgstr "Настройки уведомлений"
+
+#: MoinMoin/templates/usersettings.html:11
+#: MoinMoin/templates/usersettings.html:59
+msgid "Wiki Appearance Settings"
+msgstr "Настройки внешнего вида вики"
+
+#: MoinMoin/templates/usersettings.html:12
+#: MoinMoin/templates/usersettings.html:73
+msgid "Navigation Settings"
+msgstr "Настройки навигации"
+
+#: MoinMoin/templates/usersettings.html:13
+#: MoinMoin/templates/usersettings.html:85
+msgid "Options"
+msgstr "Параметры"
+
+#: MoinMoin/templates/usersettings.html:33
+msgid "Change Password"
+msgstr "Сменить пароль"
+
+#: MoinMoin/templates/wanteds.html:7
+#, python-format
+msgid "Total: %(total)s"
+msgstr "Всего: %(total)s"
+
+#: MoinMoin/themes/__init__.py:54
+msgid "Access denied"
+msgstr "Доступ запрещен"
+
+#: MoinMoin/themes/__init__.py:55
+msgid "You are not allowed to access this resource."
+msgstr "Вам запрещен доступ к этому ресурсу"
+
+#: MoinMoin/themes/__init__.py:286
+msgid "anonymous"
+msgstr "анонимно"
+
+#: MoinMoin/util/paramparser.py:316
+#, python-format
+msgid "Argument \"%(name)s\" must be a boolean value, not \"%(value)s\""
+msgstr "Аргумент «%(name)s» должен быть булевым значением, а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:319
+#, python-format
+msgid "Argument must be a boolean value, not \"%(value)s\""
+msgstr "Аргумент должен быть булевым значением, а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:345
+#, python-format
+msgid "Argument \"%(name)s\" must be an integer value, not \"%(value)s\""
+msgstr "Аргумент «%(name)s» должен быть целым числом, а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:348
+#, python-format
+msgid "Argument must be an integer value, not \"%(value)s\""
+msgstr "Аргумент должен быть числовым значением, а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:373
+#, python-format
+msgid "Argument \"%(name)s\" must be a floating point value, not \"%(value)s\""
+msgstr ""
+"Аргумент «%(name)s» должен быть числовым значением с плавающей точкой, а "
+"не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:376
+#, python-format
+msgid "Argument must be a floating point value, not \"%(value)s\""
+msgstr ""
+"Аргумент должен быть числовым значением с плавающей точкой, а не \"\n"
+"\"«%(value)s»"
+
+#: MoinMoin/util/paramparser.py:403
+#, python-format
+msgid "Argument \"%(name)s\" must be a complex value, not \"%(value)s\""
+msgstr "Аргумент «%(name)s» должен быть комплексным значением, а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:406
+#, python-format
+msgid "Argument must be a complex value, not \"%(value)s\""
+msgstr "Аргумент должен быть комплексным значением, а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:460
+#, python-format
+msgid "Argument \"%(name)s\" must be one of \"%(choices)s\", not \"%(value)s\""
+msgstr "Аргумент «%(name)s» должен быть один из «%(choices)s», а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:465
+#, python-format
+msgid "Argument must be one of \"%(choices)s\", not \"%(value)s\""
+msgstr "Аргумент должен быть один из «%(choices)s», а не «%(value)s»"
+
+#: MoinMoin/util/paramparser.py:690
+msgid "Too many arguments"
+msgstr "Слишком много аргументов"
+
+#: MoinMoin/util/paramparser.py:695
+msgid "Cannot have arguments without name following named arguments"
+msgstr ""
+"Нельзя использовать аргументы без имени, которые следуют за именованными "
+"аргументами"
+
+#: MoinMoin/util/paramparser.py:711
+#, python-format
+msgid "Argument \"%(name)s\" is required"
+msgstr "Требуется аргумент «%(name)s»"
+
+#: MoinMoin/util/paramparser.py:723
+#, python-format
+msgid "No argument named \"%(name)s\""
+msgstr "Нет параметров с именем «%(name)s»"
+
+
--- a/MoinMoin/user.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/user.py	Sat Jan 28 17:42:01 2012 +0100
@@ -43,7 +43,7 @@
 from MoinMoin.storage.error import NoSuchItemError, ItemAlreadyExistsError, NoSuchRevisionError
 
 
-def create_user(username, password, email, openid=None, validate=True, is_encrypted=False):
+def create_user(username, password, email, openid=None, validate=True, is_encrypted=False, is_disabled=False):
     """ create a user """
     # Create user profile
     theuser = User(auth_method="new-user")
@@ -91,6 +91,8 @@
     if validate and theuser.openid and search_users(openid=theuser.openid):
         return _('This OpenID already belongs to somebody else.')
 
+    theuser.disabled = is_disabled
+
     # save data
     theuser.save()
 
@@ -684,8 +686,11 @@
         self.save()
         return token
 
+    def validate_recovery_token(self, token):
+        return valid_token(self.recoverpass_key, token)
+
     def apply_recovery_token(self, token, newpass):
-        if not valid_token(self.recoverpass_key, token):
+        if not self.validate_recovery_token(token):
             return False
         self.recoverpass_key = None
         self.enc_password = crypt_password(newpass)
@@ -713,6 +718,28 @@
 
         subject = _('[%(sitename)s] Your wiki password recovery link',
                     sitename=self._cfg.sitename or "Wiki")
-        mailok, msg = sendmail.sendmail([self.email], subject, text, mail_from=self._cfg.mail_from)
+        mailok, msg = sendmail.sendmail(subject, text, to=[self.email], mail_from=self._cfg.mail_from)
         return mailok, msg
 
+    def mailVerificationLink(self):
+        """ Mail a user a link to verify his email address. """
+        from MoinMoin.mail import sendmail
+        token = self.generate_recovery_token()
+
+        text = _("""\
+Somebody has created an account with this email address.
+
+Please use the link below to verify your email address:
+
+%(link)s
+
+If you didn't create this account, please ignore this email.
+
+""", link=url_for('frontend.verifyemail',
+                        username=self.name, token=token, _external=True))
+
+        subject = _('[%(sitename)s] Please verify your email address',
+                    sitename=self._cfg.sitename or "Wiki")
+        mailok, msg = sendmail.sendmail(subject, text, to=[self.email], mail_from=self._cfg.mail_from)
+        return mailok, msg
+
--- a/MoinMoin/util/_tests/test_mimetype.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/util/_tests/test_mimetype.py	Sat Jan 28 17:42:01 2012 +0100
@@ -47,6 +47,8 @@
         ('.pdf',             'application/pdf'),
         ('.txt',             'text/plain'),
         ('.jpeg',            'image/jpeg'),
+        ('.png',             'image/png'),
+        ('.svg',             'image/svg+xml'),
         ('',                 'application/octet-stream')
         ]
 
--- a/MoinMoin/util/_tests/test_send_file.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/util/_tests/test_send_file.py	Sat Jan 28 17:42:01 2012 +0100
@@ -30,7 +30,7 @@
     def test_temptest(self):
         self.makefile(self.fname, 'test_content')
         result = send_file.send_file(self.fname, as_attachment = True, conditional = True)
-        expected = '<Response 12 bytes [200 OK]>'
+        expected = '<Response streamed [200 OK]>'
         assert str(result) == expected
 
         with pytest.raises(TypeError):
--- a/MoinMoin/util/_tests/test_thread_monitor.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/util/_tests/test_thread_monitor.py	Sat Jan 28 17:42:01 2012 +0100
@@ -37,10 +37,10 @@
         Monitor_test_obj = Monitor()
         # activate the hook first
         Monitor_test_obj.activate_hook()
-        f = open(self.src, "w")
-        result = Monitor_test_obj.trigger_dump(f)
+        with open(self.src, "w") as f:
+            result = Monitor_test_obj.trigger_dump(f)
         # read the content of first line
-        f = open(self.src, "r")
-        f.seek(1)
-        assert 'Dumping thread' in f.readline()
+        with open(self.src, "r") as f:
+            f.seek(1)
+            assert 'Dumping thread' in f.readline()
 
--- a/MoinMoin/util/diff_html.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/util/diff_html.py	Sat Jan 28 17:42:01 2012 +0100
@@ -20,7 +20,7 @@
         line = line[1:]
     stripped = line.lstrip()
     if len(line) - len(stripped):
-        line = "&nbsp;" * (len(line) - len(stripped)) + stripped
+        line = u"&nbsp;" * (len(line) - len(stripped)) + stripped
     #return "%d / %d / %s" % (len(line), len(stripped), line)
     return eol + line
 
@@ -53,8 +53,8 @@
             lastmatch = (match[0] + match[2], match[1] + match[2])
             continue
         llineno, rlineno = lastmatch[0]+1, lastmatch[1]+1
-        leftpane = ''
-        rightpane = ''
+        leftpane = u''
+        rightpane = u''
         linecount = max(match[0] - lastmatch[0], match[1] - lastmatch[1])
         for line in range(linecount):
             if line < match[0] - lastmatch[0]:
@@ -72,33 +72,32 @@
         if charobj.ratio() < 0.5:
             # Insufficient similarity.
             if leftpane:
-                leftresult = """<span>{0}</span>""".format(indent(escape(leftpane)))
+                leftresult = u"""<span>{0}</span>""".format(indent(escape(leftpane)))
             else:
-                leftresult = ''
+                leftresult = u''
 
             if rightpane:
-                rightresult = """<span>{0}</span>""".format(indent(escape(rightpane)))
+                rightresult = u"""<span>{0}</span>""".format(indent(escape(rightpane)))
             else:
-                rightresult = ''
+                rightresult = u''
         else:
             # Some similarities; markup changes.
             charlast = (0, 0)
-
-            leftresult = ''
-            rightresult = ''
+            leftresult = u''
+            rightresult = u''
             for thismatch in charmatch:
                 if thismatch[0] - charlast[0] != 0:
-                    leftresult += """<span>{0}</span>""".format(indent(
+                    leftresult += u"""<span>{0}</span>""".format(indent(
                         escape(leftpane[charlast[0]:thismatch[0]])))
                 if thismatch[1] - charlast[1] != 0:
-                    rightresult += """<span>{0}</span>""".format(indent(
+                    rightresult += u"""<span>{0}</span>""".format(indent(
                         escape(rightpane[charlast[1]:thismatch[1]])))
                 leftresult += escape(leftpane[thismatch[0]:thismatch[0] + thismatch[2]])
                 rightresult += escape(rightpane[thismatch[1]:thismatch[1] + thismatch[2]])
                 charlast = (thismatch[0] + thismatch[2], thismatch[1] + thismatch[2])
 
-        leftpane = '<br>'.join([indent(x) for x in leftresult.splitlines()])
-        rightpane = '<br>'.join([indent(x) for x in rightresult.splitlines()])
+        leftpane = u'<br>'.join([indent(x) for x in leftresult.splitlines()])
+        rightpane = u'<br>'.join([indent(x) for x in rightresult.splitlines()])
         result.append((llineno, leftpane, rlineno, rightpane))
 
         lastmatch = (match[0] + match[2], match[1] + match[2])
--- a/MoinMoin/util/mimetype.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/util/mimetype.py	Sat Jan 28 17:42:01 2012 +0100
@@ -12,6 +12,9 @@
 
 from MoinMoin import config
 
+# prevents unexpected results on Windows
+# see http://bugs.python.org/issue10551
+mimetypes.init(mimetypes.knownfiles)
 
 MIMETYPES_MORE = {
  # OpenOffice 2.x & other open document stuff
@@ -40,6 +43,7 @@
  '.rst': 'text/x-rst',
  '.flv': 'video/x-flv',
  '.wmv': 'video/x-ms-wmv',
+ '.wma': 'audio/x-ms-wma',
  '.swf': 'application/x-shockwave-flash',
  '.awd': 'application/x-anywikidraw',
  '.twd': 'application/x-twikidraw',
@@ -48,6 +52,8 @@
  '.moin': 'text/x.moin.wiki',
  '.creole': 'text/x.moin.creole',
  '.mediawiki': 'text/x-mediawiki',
+ '.ico': 'image/x-icon',
+ '.svg': 'image/svg+xml'
 }
 
 # add all mimetype patterns of pygments
--- a/MoinMoin/util/send_file.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/util/send_file.py	Sat Jan 28 17:42:01 2012 +0100
@@ -113,6 +113,21 @@
         mimetype = 'application/octet-stream'
 
     headers = Headers()
+
+    # We must compute size the smart way rather than letting
+    # werkzeug turn our iterable into an in-memory sequence
+    # See `_ensure_sequence` in werkzeug/wrappers.py
+    if filename:
+        fsize = os.path.getsize(filename)
+    elif file:
+        # Seek 0 bytes (0) from the end of the file (2)
+        file.seek(0, 2)
+        fsize = file.tell()
+        file.seek(0, 0)
+    else:
+        fsize = 0
+    headers.add('Content-Length', fsize)
+
     if as_attachment:
         if attachment_filename is None:
             if not filename:
--- a/MoinMoin/wikiutil.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/MoinMoin/wikiutil.py	Sat Jan 28 17:42:01 2012 +0100
@@ -240,18 +240,18 @@
         return c.upper() # we put lower and upper case words into the same index group
 
 
-def is_URL(arg, schemas=config.url_schemas):
-    """ Return True if arg is a URL (with a schema given in the schemas list).
+def is_URL(arg, schemes=config.uri_schemes):
+    """ Return True if arg is a URL (with a scheme given in the schemes list).
 
         Note: there are not that many requirements for generic URLs, basically
-        the only mandatory requirement is the ':' between schema and rest.
-        Schema itself could be anything, also the rest (but we only support some
-        schemas, as given in config.url_schemas, so it is a bit less ambiguous).
+        the only mandatory requirement is the ':' between scheme and rest.
+        Scheme itself could be anything, also the rest (but we only support some
+        schemes, as given in config.uri_schemes, so it is a bit less ambiguous).
     """
     if ':' not in arg:
         return False
-    for schema in schemas:
-        if arg.startswith(schema + ':'):
+    for scheme in schemes:
+        if arg.startswith(scheme + ':'):
             return True
     return False
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/_static/custom.css	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,36 @@
+/* Custom CSS for Sphinx */
+
+/* Import the default theme's CSS file */
+@import "default.css";
+
+.bolditalic {
+    font-weight: bold;
+    font-style: italic;
+}
+
+.sub {
+    vertical-align: sub;
+    font-size: 50%;
+}
+
+.sup {
+    vertical-align: super;
+    font-size: 50%;
+}
+
+.strikethrough {
+    text-decoration: line-through;
+}
+
+.underline {
+    text-decoration: underline;
+}
+
+/* We need borders around each cell to clearly demonstrate table boundaries
+ * in markup documentation */
+table.docutils th,
+table.docutils td {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #AAAAAA;
+}
--- a/docs/admin/backup.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/admin/backup.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -9,51 +9,50 @@
 
 .. warning::
 
-   Above procedure doesn't work. Read below about a working procedure.
+   Unfortunately, that doesn't work. Read below about a working procedure.
 
 Full Backup / Restore
 =====================
 
-Of course it is the best to have a **full** backup of your machine. That way,
-you can easily restore it to a working condition, even if things go severely
-wrong.
+Of course, it's best to have a **full** backup of your machine. If you do,
+you can easily restore it to a working condition, even if things go horribly wrong.
 
-If you have full backups, you maybe don't need the rather selective backup
-procedure described below, because your full backup already includes everything
-you need.
+However, you can use the procedure below to selectively backup only the files
+essential to your MoinMoin installation. While there is no need to maintain both a full
+and a selective backup, having one of the two is strongly advised.
 
 Selective Backup
 ================
-If you just want a backup of moin and your data, backup these files:
+If you just want a backup of MoinMoin and your data, backup the following files:
 
 * your data (most important)
 * moin configuration (e.g. wikiconfig.py)
 * logging configuration (e.g. logging.conf)
 * moin script (e.g. moin.wsgi)
 * web server configuration (e.g. apache virtualhost config)
-* optional: moin code + dependencies (at least you should know which
+* optional: moin code + dependencies (you should at least know which
   version you ran, so you can download and install that version when you
   need to restore)
 
-To create a dump of all data stored in moin (wiki items, user profiles), do
-this::
+To create a dump of all data stored in moin (wiki items, user profiles), run the
+following command::
 
  moin save --file backup.moin
 
 Please note that this file contains sensitive data (like user profiles, wiki
-contents), so store your backups at a safe place and make sure no unauthorized
-persons can access them.
+contents), so store your backups in a safe place and make sure no unauthorized
+individuals can access them.
 
 Selective Restore
 =================
 
-Restore all software and configuration files (see above) to their original
+To restore all software and configuration files (see above) to their original
 place. Make sure your (empty) wiki works as expected::
 
  moin moin -s -i  # -s = create new storage
                   # -i = create new index
 
-To load the backup file into your empty wiki, do this::
+To load the backup file into your empty wiki, run::
 
  moin load --file backup.moin
 
--- a/docs/admin/configure.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/admin/configure.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -28,7 +28,7 @@
 Do small steps and have backups
 -------------------------------
 It is a good idea to start from one of the sample configs provided with moin
-and only do small careful changes, then trying it, then doing next change.
+and only do small careful changes, then try it and then do the next change.
 
 If you're not used to the config file format, backup your last working config
 so you can revert to it in case you make some hard to find typo or other error.
@@ -124,7 +124,7 @@
 ~~~~
 To replace the default MoinMoin logo with your own logo (which is **strongly**
 recommended, especially if your wiki has private or sensitive information),
-so your users will immediately recognize which wiki site they currently use.
+so your users will immediately recognize which wiki site they are currently using.
 
 You can even use some simple text (or just nothing) for the logo, it is not
 required to be an image.
@@ -140,7 +140,7 @@
 
 Displaying license information
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If you need to display something like a license information for your content or
+If you need to display something like license information for your content or
 some other legalese, use this macro to do it::
 
     {# License information in the footer #}
@@ -150,7 +150,7 @@
 
 Inserting pieces of HTML
 ~~~~~~~~~~~~~~~~~~~~~~~~
-At some specific places, you can just add a piece of own html into the
+At some specific places, you can just add a piece of your own html into the
 head or body of the theme's html output::
 
     {# Additional HTML tags inside <head> #}
@@ -219,7 +219,7 @@
 
 Adding CSS
 ~~~~~~~~~~
-If you just want some style changes, you maybe can do them by just adding
+If you just want some style changes, you can maybe do them by just adding
 some custom css (and overwrite any style you don't like in the base theme)::
 
     {# Additional Stylesheets (after theme css, before user css #}
@@ -296,7 +296,7 @@
   
 * `anywikidraw <http://pypi.python.org/pypi/XStatic-AnyWikiDraw>`_
   a Java applet loaded from template file of modify_anywikidraw. It can be used for 
-  editing of drawings and diagrams on items.
+  editing drawings and diagrams on items.
 
 * `jquery_multi_download <http://pypi.python.org/pypi/XStatic-multiDownload>`_
   used in the template of index view for multiple parallel downloads.
@@ -340,7 +340,7 @@
 
 Custom Themes
 -------------
-In case you want to do major changes to how MoinMoin looks like (so just
+In case you want to do major changes to how MoinMoin looks (so just
 changing snippets or CSS is not enough), you could also write your own theme.
 
 Be warned: doing this is a long-term thing, you don't just have to write it,
@@ -359,7 +359,7 @@
 Authentication
 ==============
 MoinMoin uses a configurable `auth` list of authenticators, so the admin can
-configure whatever he likes to allow for authentication. Moin processes this
+configure whatever he/she likes to allow for authentication. Moin processes this
 list from left to right.
 
 Each authenticator is an instance of some specific class, configuration of
@@ -423,6 +423,9 @@
 username (like with german umlauts or accented characters). If moin does not
 crash (log a Unicode Error), you have likely found the correct coding.
 
+For users configuring GivenAuth on Apache, an example virtual host configuration
+file is included with MoinMoin in `docs/examples/deployment/moin-http-basic-auth.conf`.
+
 OpenID
 ------
 With OpenID moin can re-use the authentication done by some OpenID provider
@@ -644,29 +647,29 @@
 
 Authorization
 =============
-Moin uses Access Control Lists (ACLs) to specify who is authorized to do
-something.
+Moin uses Access Control Lists (ACLs) to specify who is authorized to perform
+a given action.
 
 Please note that wikis usually make much use of so-called *soft security*,
-that means that they are rather open and give freedom to the users, while
+which means that they are rather open and give freedom to users, while
 providing means to revert damage in case it happens.
 
-*Hard security* means to lock stuff so that no damage can happen.
+*Hard security* means that one would lock items, etc. so that no damage can possibly be done.
 
 Moin's default configuration tries to give a sane compromise of both soft
-and hard security. But, depending on the situation the wiki
-admin/owner/community has to deal with, you may need different settings.
+and hard security. Depending on the situation the wiki
+admin/owner/community has to deal with, however, you may need different settings.
 
 So just keep in mind:
 
-* if your wiki is rather open, you make it easy to contribute (like e.g. a
+* if your wiki is rather open, you might make it easy to contribute (e.g. a
   user who is not a regular user of your wiki could fix some typos he has just
-  found). But: a hostile user (or bot) also might put some spam into your wiki
-  (you can revert the spam later).
-* if you are rather closed (like requiring every user to first apply for an
-  account and to log in before being able to do changes), you'll never get
+  found). However, a hostile user (or bot) might also put some spam into your wiki
+  (you'd be able to revert the spam later).
+* if your wiki is rather closed (e.g. you require every user to first apply for an
+  account and to log in before being able to do changes), you'll rarely get
   contributions from casual users and maybe also less from members of your
-  community. But: likely you won't get spam either.
+  community. But, it's likely you won't get spam either.
  
 
 ACL for functions
@@ -680,16 +683,16 @@
 
 Supported capabilities (rights):
 
-* superuser - used for misc. administrative functions, give this only to
+* superuser - used for miscellaneous administrative functions. Give this only to
   highly trusted people
-* notextcha - if you have TextChas enabled, users with notextcha capability
+* notextcha - if you have TextChas enabled, users with the notextcha capability
   won't get questions to answer. Give this to known and trusted users who
   regularly edit in your wiki.
 
 ACLs for contents
 -----------------
 These ACLs control access to contents stored in the wiki - they are configured
-per storage backend (see also there) and (optionally) in the metadata of wiki
+per storage backend (see storage backend docs) and optionally in the metadata of wiki
 items::
 
     # we just show the default value of acl_rights_contents for information,
@@ -715,15 +718,15 @@
 
 * `before` is usually used to force stuff (e.g. if you want to give some
   wiki admin all permissions no matter what)
-* `default` is behaviour if nothing special has been specified (no ACL in
+* `default` is the behavior if nothing special has been specified (no ACL in
   item metadata)
-* `after` is (rarely) used to "not forget something unless otherwise specified".
+* `after` is rarely used. When it is, it's used to "not forget something unless otherwise specified".
 
 When configuring content ACLs, you can choose between standard (flat) ACL
 processing and hierarchic ACL processing. Hierarchic processing means that
-subitems inherit ACLs from their parent items if they have no own ACL.
+subitems inherit ACLs from their parent items if they don't have an ACL.
 
-Note: while hierarchic ACLs are rather convenient sometimes, they make the
+Note that while hierarchic ACLs are rather convenient sometimes, they make the
 system more complex. You have to be very careful with potential permissions
 changes happening due to changes in the hierarchy, like when you create,
 rename or delete items.
@@ -733,34 +736,31 @@
 * read - read content
 * write - write (edit, modify) content
 * create - create new items
-* destroy - completely destroy revisions or items (never give this to not
-  fully trusted users)
-* admin - change (create, remove) ACLs for the item (never give this to not
-  fully trusted users)
+* destroy - completely destroy revisions or items (give this only to *fully-trusted* users)
+* admin - change (create, remove) ACLs for the item (give this only to *fully-trusted* users)
 
 ACLs - special groups
 ---------------------
-Additionally to the groups provided by the group backend(s), there are some
+In addition to the groups provided by the group backend(s), there are some
 special group names available within ACLs:
 
 * All - a virtual group containing every user
 * Known - a virtual group containing every logged-in user
-* Trusted - a virtual group containing every logged-in user, who was logged
-  in by some specific "trusted" authentication methods
+* Trusted - a virtual group containing every logged-in user who was logged
+  in by some specific "trusted" authentication method
 
 
 ACLs - basic syntax
 -------------------
-An ACL is a (unicode) string with one or multiple access control entries
-(space separated).
-
-An entry has:
+An ACL is a unicode string with one or more access control entries
+which are space separated.
 
-* a left side with user and/or group names (comma separated)
-* a colon ':' as separator and
-* a right side with rights / capabilities (comma separated).
+An entry is a colon-separated set of two values::
 
-An ACL is processed from left to right, first left-side match counts.
+* the left side is a comma-separated list of user and/or group names and
+* the right side is a comma-separated list of rights / capabilities for those users/groups.
+
+An ACL is processed from left to right, where the first left-side match counts.
 
 Example::
 
@@ -773,10 +773,10 @@
 
 If "JoeDoe" is currently logged in and moin processes this ACL, the first entry
 won't match, so moin will proceed left-to-right and look at the second entry.
-Here we have the special group name "All" (and JoeDoe obviously is a member of
-this group), so we have a match here.
+Here we have the special group name, "All" (and JoeDoe is obviously a member of
+this group), so this entry matches.
 If moin wants to know whether he may destroy, the answer will be "no", as
-destroy is not listed on the right side of this entry. If moin wants to know
+destroy is not listed on the right side of the "All" entry. If moin wants to know
 whether he may write, the answer will be "yes".
 
 Notes:
@@ -784,7 +784,7 @@
 * As a consequence of the left-to-right and first-match-counts processing,
   you must order ACL entries so that the more specific ones (like for
   "SuperMan") are left of the less specific ones.
-  Usually you want this order:
+  Usually, you want this order:
 
   1) usernames
   2) special groups
@@ -793,23 +793,24 @@
   5) Known
   6) All
 
-* Do not put any spaces into an ACL entry (except if it is part of a user or
-  group name)
+* Do not put any spaces into an ACL entry, unless it is part of a user or
+  group name.
 
 * A right that is not explicitly given by an applicable ACL is denied.
 
-* For most ACLs there are builtin defaults, which give some rights.
+* For most ACLs there are built-in defaults which give some limited rights.
 
 ACLs - entry prefixes
 ---------------------
-To make the system more flexible, there are also two ACL entry modifiers: the prefixes '+' and '-'.
+To make the system more flexible, there are two ways to modify an ACL entry: prefixing it with a '+' or a '-'.
 
-If you use them, matches will have to be left-side *and* right-side (otherwise
-it will just continue with the next entry).
+If you use one of the two, MoinMoin will search for both a username and permission, and a match will have to match
+both the name of user (left-side) *and* the permission MoinMoin is searching for (right-side), otherwise
+it will just continue with the next entry.
 
-'+' means to give the permission(s) specified on the right side.
+'+' indicates that MoinMoin should give the permission(s) specified on the right side.
 
-'-' means to deny the permission(s) specified on the right side.
+'-' indicates that MoinMoin should deny the permission(s) specified on the right side.
 
 Example::
 
@@ -831,16 +832,16 @@
 
 Notes:
 
-* you usually use these modifiers if most of the rights shall be as specified
+* you usually use these modifiers if most of the rights for a given user shall be specified
   later, but a special user or group should be treated slightly different for
   a few special rights.
 
 ACLs - Default entry
 --------------------
-There is a special ACL entry "Default" which expands itself in-place to the
+There is a special ACL entry, "Default", which expands itself in-place to the
 default ACL.
 
-This is useful if e.g. for some items you mostly want the default ACL, but
+This is useful, for example, if when you mostly want the default ACL, but
 with a slight modification - but you don't want to type in the default ACL
 all the time (and you also want to be able to change the default ACL without
 having to edit lots of items).
@@ -863,9 +864,9 @@
 
 Features:
 
-* when registering a user or saving an item, ask a random question
+* when registering a user or saving an item, it can ask a random question
 * match the given answer against a regular expression
-* q and a can be configured in the wiki config
+* questions and answers can be configured in the wiki config
 * multi language support: a user gets a textcha in his language or in
   language_default or in English (depending on availability of questions and
   answers for the language)
@@ -877,9 +878,9 @@
 
 * have 1 word / 1 number answers
 * ask questions that normal users of your site are likely to be able to answer
-* do not ask too hard questions
+* do not ask overly complex questions
 * do not ask "computable" questions, like "1+1" or "2*3"
-* do not ask too common questions
+* do not ask overly obvious questions
 * do not share your questions with other sites / copy questions from other
   sites (or spammers might try to adapt to this) 
 * you should at least give textchas for 'en' (or for your language_default, if
@@ -908,7 +909,7 @@
 
 Secrets
 =======
-Moin uses secrets (just use a long random strings, don't reuse any of your
+Moin uses secrets (just use a long random string; *not* a reuse of any of your
 passwords) to encrypt or cryptographically sign some stuff like:
 
 * textchas
@@ -931,21 +932,21 @@
 Moin can get group and dictionary information from some supported backends
 (like the wiki configuration or wiki items).
 
-A group is just a list of unicode names. It can be used for any application,
+A group is just a list of unicode names. It can be used for any application:
 one application is defining user groups for usage in ACLs.
 
 A dict is a mapping of unicode keys to unicode values. It can be used for any
-application, currently it is not used by moin itself.
+application. Currently, it is not used by moin itself.
 
 Group backend configuration
 ---------------------------
-WikiGroups backend gets groups from wiki items and is used by default::
+The WikiGroups backend gets groups from wiki items and is used by default::
 
     def groups(self, request):
         from MoinMoin.datastruct import WikiGroups
         return WikiGroups(request)
 
-ConfigGroups uses groups defined in the configuration file::
+The ConfigGroups backend uses groups defined in the configuration file::
 
     def groups(self, request):
         from MoinMoin.datastruct import ConfigGroups
@@ -954,7 +955,7 @@
                   u'AdminGroup': [u'Admin1', u'Admin2', u'John']}
         return ConfigGroups(request, groups)
 
-CompositeGroups to use both ConfigGroups and WikiGroups backends::
+CompositeGroups can use, for the most part, any combination of backends. The following is an example of using the ConfigGroups and WikiGroups backends::
 
     def groups(self, request):
         from MoinMoin.datastruct import ConfigGroups, WikiGroups, CompositeGroups
@@ -972,13 +973,13 @@
 Dict backend configuration
 --------------------------
 
-WikiDicts backend gets dicts from wiki items and is used by default::
+The WikiDicts backend gets dicts from wiki items and is used by default::
 
     def dicts(self, request):
         from MoinMoin.datastruct import WikiDicts
         return WikiDicts(request)
 
-ConfigDicts backend uses dicts defined in the configuration file::
+The ConfigDicts backend uses dicts defined in the configuration file::
 
     def dicts(self, request):
         from MoinMoin.datastruct import ConfigDicts
@@ -988,7 +989,7 @@
                                   u'2': 'Two'}}
         return ConfigDicts(request, dicts)
 
-CompositeDicts to use both ConfigDicts and WikiDicts::
+The CompositeDicts backend can use any combination of backends. The following is an example of using the ConfigDicts and WikiDicts backends::
 
     def dicts(self, request):
         from MoinMoin.datastruct import ConfigDicts, WikiDicts, CompositeDicts
@@ -1003,13 +1004,13 @@
 
 Storage
 =======
-MoinMoin supports storage backends for different ways of storing wiki items.
+MoinMoin supports storage backends as different ways of storing wiki items.
 
 Setup of storage is rather complex and layered, involving:
 
 * a router middleware that dispatches parts of the namespace to the respective
   backend
-* ACL checking middlewares that make sure nobody accesses something he is not
+* ACL checking middlewares that make sure nobody accesses something he/she is not
   authorized to access
 * Indexing mixin that indexes some data automatically on commit, so items can
   be selected / retrieved faster.
@@ -1017,7 +1018,7 @@
 
 create_simple_mapping
 ---------------------
-This is a helper function to make storage setup easier - it helps you to:
+This is a helper function to make storage setup easier - it helps you:
 
 * create a simple setup that uses 3 storage backends internally for these
   parts of the namespace:
@@ -1044,13 +1045,13 @@
     )
 
 The `uri` depends on the kind of storage backend and stores you want to use
-(see below). Usually it is a URL-like string that looks like::
+(see below). Usually it is a URL-like string in the form of::
 
     stores:fs:/srv/mywiki/%(nsname)s/%(kind)s
     
 `stores` is the name of the backend, followed by a colon, followed by a store
 specification. `fs` is the name of the store, followed by a specification
-that makes sense for the fs (filesystem) store (== a path with placeholders).
+that makes sense for the fs (filesystem) store (i.e. a path with placeholders).
 
 `%(nsname)s` placeholder will be replaced 'content' or 'userprofiles' for
 the respective backend. `%(kind)s` will be replaced by 'meta' or 'data'
@@ -1076,7 +1077,7 @@
 
 * protects access to lower storage layers by ACLs (Access Control Lists)
 * makes sure there won't be ACL security issues, even if upper layers have bugs
-* if you use create_simple_mapping, you just give the ACL parameters, the
+* if you use create_simple_mapping, you just give the ACL parameters. The
   middleware will be set up automatically by moin.
 
 routing middleware
@@ -1084,7 +1085,7 @@
 Features:
 
 * dispatches storage access to different backends depending on the item name
-* in POSIX terms: something fstab/mount-like
+* in POSIX terms, it's something like fstab/mount
 * if you use create_simple_mapping, the router middleware will be set up
   automatically by moin.
 
@@ -1094,7 +1095,7 @@
 
 * maintains an index for important metadata values
 * speeds up looking up / selecting items
-* makes it possible that lower storage layers can be simpler
+* makes it possible for lower storage layers to be simpler
 * the indexing middleware will be set up automatically by moin.
 
 stores backend
@@ -1129,9 +1130,9 @@
 ----------
 Features:
 
-* stores data into a (SQL) database / table
-* uses slqalchemy (without ORM) as database abstraction
-* supports multiple types of databases, like:
+* stores data into an (SQL) database / table
+* uses slqalchemy (without the ORM) for database abstraction
+* supports multiple types of databases. For example:
  
   - sqlite (default, comes built-into Python)
   - postgresql
@@ -1153,7 +1154,7 @@
 --------
 Features:
 
-* uses a Kyoto Cabinet file to store
+* uses a Kyoto Cabinet file for storage
 * very fast
 * single-process only, local only
 
@@ -1163,8 +1164,8 @@
 
 Please see the kyoto cabinet docs about the part after `kc:`.
 
-If you use kc with the builtin server of moin, you must not use the reloader,
-but disable it by commandline option::
+If you use kc with the builtin server of moin, you cannot use the reloader.
+Disable it with the commandline option::
 
   moin moin -r
 
@@ -1173,7 +1174,7 @@
 --------
 Features:
 
-* uses a Kyoto Tycoon server to store
+* uses a Kyoto Tycoon server for storage
 * fast
 * multi-process, local or remote.
 
@@ -1216,17 +1217,17 @@
 
 Sending E-Mail
 --------------
-Moin can optionally send E-Mail, e.g. to:
+Moin can optionally send E-Mail. Possible uses:
 
 * send out item change notifications.
 * enable users to reset forgotten passwords
 
-You need to configure some stuff before sending E-Mail can be supported::
+You need to configure some settings before sending E-Mail can be supported::
 
     # the "from:" address [Unicode]
     mail_from = u"wiki <wiki@example.org>"
 
-    # a) using a SMTP server, e.g. "mail.provider.com" (None to disable mail)
+    # a) using an SMTP server, e.g. "mail.provider.com" (None to disable mail)
     mail_smarthost = "smtp.example.org"
 
     # if you need to use SMTP AUTH at your mail_smarthost:
@@ -1242,6 +1243,18 @@
    describe more moin configuration
 
 
+User E-Mail Address Verification
+--------------------------------
+
+At account creation time, Moin can require new users to verify their E-Mail
+address by clicking a link that is sent to them.
+
+Make sure that Moin is able to send E-Mails (see previous section) and add the
+following line to your configuration file to enable this feature::
+
+    user_email_verification = True
+
+
 =======================
 Framework Configuration
 =======================
@@ -1268,8 +1281,8 @@
 =====================
 
 By default, logging is configured to emit output on `stderr`. This will work
-OK for the builtin server (will just show on the console) or for e.g. Apache2
-(will be put into error.log).
+well for the built-in server (it will just show up on the console) or for Apache2 and similar
+(logging will be put into error.log).
 
 Logging is very configurable and flexible due to the use of the `logging`
 module of the Python standard library.
--- a/docs/admin/index.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/admin/index.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -4,19 +4,18 @@
 
 General
 =======
-moin strongly relies on indexes that accelerate access to item metadata and
-data and makes it possible to have simple backends, because the index layer
+MoinMoin relies strongly on indexes that accelerate access to item metadata and
+data, and make it possible to have simple backends, because the index layer
 is doing all the hard and complex work.
 
 Indexes are used internally for many operations like item lookup, history,
-iterating over items, search (also for interactive searches), etc..
+iterating over items, search (also for interactive searches), etc.
 
-moin won't be able to start with damaged, inaccessible or non-existing indexes.
-
-So, you need to configure and initialize indexing correctly first.
+MoinMoin won't be able to start with damaged, inaccessible or non-existing indexes.
+As a result, you'll need to configure and initialize indexing correctly first.
 
 moin will automatically update the index when items are created, updated, deleted,
-destroyed or renamed (via the storage api of moin, indexing layer or above).
+destroyed, or renamed (via the storage api of moin, indexing layer or above).
 
 Configuration
 =============
@@ -28,8 +27,8 @@
 
 **Note:**
 * The path MUST be absolute.
-* Moin will use `index_dir`.temp location also, if you build an index at
-the so-called `temporary location`.
+* Moin will use `index_dir`.temp location as well, if you build an index at
+the `temporary location`.
 
 
 moin index script reference
@@ -43,26 +42,26 @@
 
 moin index-create
 -----------------
-Creates an empty, but valid index.
+Creates an empty but valid index.
 
-**Note:** the moin wsgi application needs an index to successfully start up.
-As the moin index-* script commands are also based on the moin wsgi application,
-this can lead to "which came first, the chicken or the egg?" like problems.
+**Note:** the moin WSGI application needs an index to successfully start up.
+As the moin index-* script commands are also based on the moin WSGI application,
+this can lead to a chicken and the egg problem.
 
 To solve this, the moin command has a ``-i`` (``--index-create``) option that
 will trigger index creation on startup.
 
-Additionally, if the storage is also non-existant yet, one might also need
+Additionally, if the storage is also non-existent yet, one might also need
 ``-s`` (``--storage-create``) to create an empty storage on startup.
 
 moin index-build
 ----------------
-Process all revisions of this wiki and add the indexable documents to the index.
+Process all revisions of the wiki and add the indexable documents to the index.
 
 **Note:**
-* For big wikis, this can take rather long, consider using --tmp.
+* For big wikis, this can take rather long; consider using --tmp.
 * index-build does NOT clear the index at the beginning.
-* index-build does not check the current contents of the index, you must not run
+* index-build does not check the current contents of the index. Therefore you must not run
   index-build multiple times for the same data / same wiki.
 
 moin index-update
@@ -70,13 +69,13 @@
 Compare an index to the current storage contents and update the index as
 needed (add, remove, update stuff) to reflect the current storage contents.
 
-**Note:** You can use this after building at the tmp location, to also get
-the changes that happened to the wiki while building the index. You can run
-index-update multiple times to increasingly catch up.
+**Note:** You can use this after building at the tmp location to get
+the changes that happened to the wiki while building the index as well. You can run
+index-update multiple times to keep even more caught up.
 
 moin index-destroy
 ------------------
-Destroy an index (nothing left at the respective location).
+Destroy an index, such that nothing left at the respective location.
 
 moin index-move
 ---------------
@@ -84,7 +83,7 @@
 
 moin index-optimize
 -------------------
-Optimize an index, see Whoosh docs for more details.
+Optimize an index:: see Whoosh docs for more details.
 
 moin index-dump
 ---------------
@@ -103,15 +102,15 @@
     moin index-create --storage-create --index-create
     moin index-create -s -i  # same, but shorter
 
-Storage and index is now initialized (both empty).
+Storage and index are now initialized (both empty).
 
-If you add stuff to your wiki, the index will get updated on the fly.
+If you add data to your wiki, the index will get updated on the fly.
 
 
 If your wiki has data and is shut down
 --------------------------------------
 If index needs a rebuild for some reason (e.g. index lost, index damaged,
-incompatible upgrade, ...), use::
+incompatible upgrade, etc.), use::
 
     moin index-create -i
     moin index-build  # can take a while...
@@ -130,28 +129,27 @@
      # start the wiki again (or allow changes now again)
 
 **Note:** Indexing puts load onto your server, so if you like to do regular
-index rebuilds, schedule them at some time when your server is not too busy
-otherwise.
+index rebuilds, schedule them at some time when your server is not too busy.
 
 
 Building an index for a wiki farm
 =================================
-If you run a wiki farm (multiple, but related wikis), you may share the index
-between the farm wikis, so farm wiki users will be able to search in one wiki
+If you run a wiki farm (multiple related wikis), you may share the index
+between the wikis, so users will be able to search in one wiki
 and also see results from the others.
 
-Before start you must prepare your wiki configs.
+Before you start, you must prepare your wiki configs.
 
-For example, imagine some company uses 2 farm wikis: ``Sales``, ``Engineering``
+For example, imagine a company that uses 2 "farm" wikis: ``Sales``, ``Engineering``
 
-So, wiki configs will be looking like 
+Their respective wiki configs would look like:
 
-wiki config for ``Sales``::
+``Sales``::
 
       interwikiname = u"Sales"
       index_dir = "/path/to/wiki/index"
 
-wiki config for ``Engineering``::
+``Engineering``::
 
       interwikiname = u"Engineering"
       index_dir = "/path/to/wiki/index"
@@ -165,6 +163,6 @@
 
 Now you should have a shared index for all these wikis.
 
-**Note:** Do not build indexes for multiple wikis in parallel, this is not
+**Note:** Do not build indexes for multiple wikis in parallel. This is not
 supported.
 
--- a/docs/admin/install.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/admin/install.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -4,20 +4,23 @@
 
 Downloading
 ===========
-For moin2, there is currently no packaged download available, you have to get
-it from the repository:
-
-Alternative 1a (using Mercurial DVCS)::
-
- $ hg clone http://hg.moinmo.in/moin/2.0 moin-2.0
+For moin2, there is currently no packaged download available, so you have to get
+it from the repository.
+You can use one of two repository URLs (there is little difference between them),
+and either use Mercurial to keep a constantly updated copy of the code or download an archive of the files in tar.gz format:
 
-Alternative 1b (using Mercurial DVCS)::
+Using Mercurial to clone one of our repos::
 
- $ hg clone http://bitbucket.org/thomaswaldmann/moin-2.0 moin-2.0
+ hg clone http://hg.moinmo.in/moin/2.0 moin-2.0
+ OR
+ hg clone http://bitbucket.org/thomaswaldmann/moin-2.0 moin-2.0
 
-Alternative 2:
-Visit http://hg.moinmo.in/moin/2.0 with your web browser, download the tgz
-and unpack it.
+Now make sure your work dir is using the default branch::
+
+ hg up -C default
+
+Alternatively, visit http://hg.moinmo.in/moin/2.0 with your web browser and download the archive
+(usually for the "default" branch) and unpack it.
 
 Installing
 ==========
@@ -25,110 +28,128 @@
 
 Developer install
 -----------------
+Using your standard Python install or a virtualenv directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please make sure you have `virtualenv` installed (it includes `pip`).
 
-If you just want to run moin in-place in your mercurial workdir, with your
-normal system Python, run this from your mercurial moin2 work dir (you should
-do this using your normal user login, no root or Administrator privileges needed)::
+If you just want to run moin in-place in your mercurial working directory
+with your normal system installation of Python, run the following command
+from your mercurial moin2 working directory (you should not run this as an
+administrator/root user; use your standard user account)::
 
- ./quickinstall  # for linux (or other posix OSes)
+ ./quickinstall  # for Linux (or other posix OS's)
  # or
  quickinstall.bat  # for windows
 
-This will use virtualenv to create a directory `env/` and create a virtual
-environment for moin there and then install moin2 including all dependencies
-into that directory.
-pip will fetch all dependencies from pypi and install them, so this may take
-a while.
+This will use virtualenv to create a directory `env/` within the current directory,
+create a virtual environment for MoinMoin and then install moin2 including all dependencies into that directory.
+`pip` will automatically fetch all dependencies from PyPI and install them, so this may take a while.
 It will also compile the translations (`*.po` files) to binary `*.mo` files.
 
-Please review the output of the quickinstall script, whether there were fatal
-errors. In case you have a bad network connection that makes the downloads
-fail, you can try to do the steps in quickinstall manually.
+Please review the output of the quickinstall script, and check whether there were fatal errors.
 
-Further, it will create a "moin" script for your platform which you can use
-for starting moin (the builtin server) or invoke moin script commands.
-After you activated the virtual environment, the moin script will be in the
-PATH, so you can just type "moin" on the shell / cmd.
+If you have a bad network connection that makes the downloads fail, you can try to do the steps in quickinstall manually.
 
-Note: in this special mode, it won't copy the MoinMoin code to the env/
-directory, it will run everything from your work dir, so you can modify code
-and directly try it out (you only need to do this installation procedure once).
+Further, the quickinstall script will create a `moin` script for your
+platform which you can use for starting the built-in server or invoke moin script commands.
+
+After you activated the virtual environment, the built-in server script (named `moin`) will be in the standard PATH,
+so you can just run the command `moin` verbatim on the command line.
+
+Note: in this special mode, it won’t copy the MoinMoin code to the env/ directory,
+it will run everything from your work dir, so you can modify code and directly try it out
+(you only need to do this installation procedure once).
 
 Using a different Python or a different virtualenv directory
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-See the `quickinstall` script and just modify these lines as needed before
-running it::
-
-    DIR=env
-    PYTHON=python
-
-E.g. if you want to use `pypy` and name the virtualenv directory `env-pypy`,
+For example, if you want to use `PyPy` and want to name the virtualenv directory `env-pypy`,
 use::
 
+    # for linux
     DIR=env-pypy
     PYTHON=/opt/pypy/bin/pypy
 
-That way, you can have all sorts of Pythons in different virtualenv directories
-within your moin2 workdir.
-
+That way, you can test with different versions of Python in different virtualenv directories within your moin2 workdir.
 
-Entering the virtual env
-------------------------
-Enter your virtual environment::
+Activating the virtual env
+--------------------------
 
- source env/bin/activate
+ source env/bin/activate  # for linux (or other posix OSes)
+ # or
+ env\Scripts\activate.bat  # for windows
 
-Initializing the storage and the index
---------------------------------------
-Creates an (empty) storage and an (empty) index::
+IMPORTANT: you always need to activate the virtual environment before running
+anything that executes moin code! Otherwise it won't find the moin command,
+nor the moin code nor the libraries it needs. Also, if you want to install
+additional software into the virtual environment, activate it before running pip!
 
- moin index-create -s -i
+Initializing index and/or storage
+---------------------------------
+If you have an existing storage AND a valid index (for this storage’s content, and for this moin version),
+you can skip this section.
+
+If you start from scratch (no storage created yet, and no index created yet),
+you need to create an (empty) storage and an (empty) index::
+
+ moin index-build -s -i
+
+If you already have an existing storage, but no index yet::
+
+ moin index-build -i
 
 Loading some items
 ------------------
-In case you do not want to have a completely empty wiki, you may want to load
-some items into it. We provide some in `contrib/serialized` directory and you
+If you don't want to have a completely empty wiki, you may want to load
+some default items into it. We provide some in the `contrib/serialized` directory and you
 can load them like this::
 
  # load some example items:
  moin load --file contrib/serialized/preloaded_items.moin
 
 .. todo::
-   example items file is missing, build one.
+   Example items file is missing, and we still need to build one.
 
 Installing PIL
 ~~~~~~~~~~~~~~
-For some image processing functions (like resizing, rotating) of moin, you
-need PIL (Python Imaging Library). If you install it with pip, it'll try to
-find some jpeg support library and development headers on your system and
-in case you don't have that, there will be no jpeg support in PIL.
+For some image processing functions that MoinMoin uses (like resizing, rotating),
+you need PIL (Python Imaging Library).
 
+Windows users who want to install PIL should skip the remainder of this section and read
+Troubleshooting -- PIL Installation Under Windows below.
+
+If you install PIL with pip, pip will try to find a jpeg support library and associated development
+headers on your system and if you do not have that, there will be no jpeg support in PIL.
 So, if you want jpeg support, make sure you have the jpeg libs/headers::
 
  # install jpeg library and development headers:
- sudo apt-get install libjpeg62-dev  # ubuntu / debian
- yum install libjpeg-turbo-devel  # fedora / red hat
-
-Now install PIL into your virtual environment::
+ sudo apt-get install libjpeg62-dev  # Ubuntu / Debian-based
+ yum install libjpeg-turbo-devel  # Fedora / Redhat-based
 
- # enter your virtual environment:
- source env/bin/activate
+Now activate your virtual environment and install PIL into it::
 
- # install Python Imaging Library:
- pip install pil
+ pip install pil # for linux (or other posix OSes)
 
 Troubleshooting
-~~~~~~~~~~~~~~~
-If you have a bad or limited network connection, you may run into trouble
-with the commands issued by the quickinstall script.
+---------------
 
-You may see tracebacks from pip, timeout errors, etc. (see the output of the
-quickinstall script).
+PyPi down
+~~~~~~~~~
+Now and then, PyPi might be down or unreachable.
+There are mirrors b.pypi.python.org, c.pypi.python.org, d.pypi.python.org
+you can use in such cases, you just need to tell pip to do so:
 
-If this is the case, try it manually::
+ # put this into ~/.pip/pip.conf
+ [global]
+ index-url = http://c.pypi.python.org/simple
 
+Bad Network Connection
+~~~~~~~~~~~~~~~~~~~~~~
+If you have a poor or limited network connection, you may run into trouble with the commands issued by
+the quickinstall script.
+You may see tracebacks from pip, timeout errors, etc. (see the output of the quickinstall script).
+
+If this is the case, try it manually:
  # enter your virtual environment:
  source env/bin/activate
 
@@ -137,15 +158,23 @@
 
 Now install each package into your virtual env manually:
 
-* Find the required packages by looking into setup.py (see install_requires).
-* Download the package from http://pypi.python.org/
+* Find the required packages by looking at "install_requires" within `setup.py`.
+* Download each required package from http://pypi.python.org/
 * Install each of them individually by::
- 
+
     pip install package.tar
 
 * Now try again::
 
     pip install -e .
 
-Repeat these steps until you don't see fatal errors any more.
+Repeat these steps until you don't see fatal errors.
 
+PIL Installation Under Windows
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+PIL version 1.1.7 does not install correctly via "pip install pil" on Windows.
+Some users have had success using "pip install pillow" (a fork of PIL fixing
+a packaging issue).  Other users have resorted to installing PIL 1.1.6 in the
+main Python directory using the Windows installers available at
+http://www.pythonware.com/products/pil/
+
--- a/docs/admin/requirements.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/admin/requirements.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -5,27 +5,26 @@
 MoinMoin requires Python >= 2.6 and < 3.0.
 We usually test using CPython and this is what we recommend.
 
-You can also try PyPy - PyPy >= 1.6 seems to work OK with moin.
-Hint: modify the quickinstall script so it uses PYTHON=pypy.
+You can also try PyPy: PyPy >= 1.6 seems to work with moin.
 
 Servers
 =======
 
-You can use anything that speaks WSGI to moin:
+For moin, you can use any server compatible with WSGI:
 
-* the builtin "moin" server (recommended for desktop wikis, testing,
-  debugging, development, adhoc-wikis)
-* apache with mod_wsgi (recommended for bigger/busier wikis)
-* other WSGI-compatible servers or middlewares
-* For cgi, fastcgi, scgi, ajp, ... you can use the "flup" middleware:
+* the builtin "moin" server is recommended for desktop wikis, testing,
+  debugging, development, adhoc-wikis, etc.
+* apache with mod_wsgi is recommended for bigger/busier wikis.
+* other WSGI-compatible servers or middlewares are usable
+* For cgi, fastcgi, scgi, ajp, etc., you can use the "flup" middleware:
   http://trac.saddi.com/flup
-* IIS with ISAPI-WSGI gateway: http://code.google.com/p/isapi-wsgi/
+* IIS with ISAPI-WSGI gateway is also compatible: http://code.google.com/p/isapi-wsgi/
 
 
 Dependencies
 ============
 
-For dependency informations, please see setup.py.
+For dependency information, please see setup.py.
 
 If you use easy_install or pip (or our ``quickinstall`` script),
 dependencies are usually automatically dealt with.
--- a/docs/admin/serve.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/admin/serve.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -2,52 +2,43 @@
 Server options
 ==============
 
-Builtin Web Server (easy)
-=========================
-Moin comes with a simple builtin web server (provided by werkzeug), which
-is suitable for development, debugging, personal and small group wikis.
+Built-in Web Server (easy)
+==========================
+Moin comes with a simple built-in web server (powered by Werkzeug), which
+is suitable for development, debugging, and personal and small group wikis.
 
-It is not made for serving bigger loads, but it is easy to use.
+It is *not* made for serving bigger loads, but it is easy to use.
 
-Please note that by default the builtin server uses port 8080. As this is
->1024, root (Administrator) privileges are not required and we strongly
-recommend that you just use a normal (unprivileged) user account. If you
+Please note that by default the built-in server uses port 8080. As this is
+above port 1024, root (Administrator) privileges are not required and we strongly
+recommend that you use a normal (unprivileged) user account instead. If you
 are running a desktop wiki or doing moin development, just use your normal
 login user.
 
-Entering the virtual env
-------------------------
-If you installed to a virtualenv, you need to activate it first, so it will
-find the moin script, the moin code and all its library dependencies::
-
- source env/bin/activate  # for linux (or other posix OSes)
- # or
- call env\bin\activate  # for windows
-
-Running the builtin server
---------------------------
-Then you can run the moin builtin server by::
+Running the built-in server
+---------------------------
+Then you can run the moin built-in server by::
 
  moin
  # or, if you need another ip/port:
  moin moin --config /path/to/wikiconfig.py --host 1.2.3.4 --port 7777
 
-Now moin starts the builtin server and tries to locate the wiki configuration
-from (please use an absolute path):
+MoinMoin will start the built-in server and try to locate the wiki configuration
+from one of the following: **NOTE: please use an absolute path**
 
-- commandline argument `--config /path/to/wikiconfig.py`
+- command line argument `--config /path/to/wikiconfig.py`
 - environment variable `MOINCFG=/path/to/wikiconfig.py`
 - current directory, file `wikiconfig_local.py`
 - current directory, file `wikiconfig.py`
 
-While the moin server is starting up, you will see some log output like::
+While the moin server is starting up, you will see some log output, for example::
 
  2011-03-06 23:35:11,445 INFO werkzeug:116  * Running on http://127.0.0.1:8080/
 
 Now point your browser at that URL - your moin wiki is running!
 
-Stopping the builtin server
----------------------------
+Stopping the built-in server
+----------------------------
 To stop the wiki server, either use `Ctrl-C` or close the window.
 
 
@@ -58,7 +49,7 @@
 server administration requires advanced experience with the operating system,
 permissions management, dealing with security, the server software, etc.
 
-What you need to achieve is that your web server can talk to the moin WSGI
+In order to use MoinMoin with another web server, ensure that your web server can talk to the moin WSGI
 application, which you can get using this code::
 
  from MoinMoin.app import create_app
@@ -67,18 +58,18 @@
 MoinMoin is a Flask application (Flask is a micro framework for WSGI apps),
 so we recommend you just read Flask's good deployment documentation.
 
-Just make sure you use `create_app()` as shown above to create the
+Make sure you use `create_app()` as shown above to create the
 application (you can't just import the application from MoinMoin).
 
-Continue reading there: http://flask.pocoo.org/docs/deploying/
+Continue reading here: http://flask.pocoo.org/docs/deploying/
 
 In case you run into trouble with deployment of the moin WSGI application,
-you can try a simpler WSGI app first, see `docs/examples/deployment/test.wsgi`.
+you can try a simpler WSGI app first. See `docs/examples/deployment/test.wsgi`.
 
-As long as you can't make `test.wsgi` work, you do not have a problem with
-moin, but with your web server and WSGI app deployment method.
+As long as you can't make `test.wsgi` work, the problem is not with
+moin, but rather with your web server and WSGI app deployment method.
 
-If the test app starts doing something else than Server Error 500, please
+When the test app starts doing something other than Server Error 500, please
 proceed with the MoinMoin app and its configuration.
 Otherwise, read your web server error log files.
 
--- a/docs/admin/upgrade.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/admin/upgrade.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -3,29 +3,29 @@
 =========
 
 .. note::
-   moin2 is internally working very differently compared to moin 1.x.
+   Internally, moin2 is very different than moin 1.x.
 
    moin 2.0 is *not* just a +0.1 step from 1.9 (like 1.8 -> 1.9), but the
-   change of the major revision is indicating *major and incompatible changes*.
+   change of the major version number is indicating *major and incompatible changes*.
 
-   So please consider it to be a different, incompatible software that tries
-   to be compatible at some places:
+   So please consider it to be a different and incompatible piece of software that tries
+   to be compatible in some areas:
 
    * Server and wiki engine Configuration: expect to review/rewrite it
    * Wiki content: expect 90% compatibility for existing moin 1.9 content. The
      most commonly used simple moin wiki markup (like headlines, lists, bold,
      ...) will still work, but expect to change macros, parsers, action links,
-     3rd party extensions (for example).
+     3rd party extensions, for example.
 
 From moin < 1.9
 ===============
 As MoinMoin 1.9.x has been out there for quite a while, we only describe how
 to upgrade from moin 1.9.x to moin2. If you still run an older moin
-version than this, please first upgrade to moin 1.9.x. Maybe run 1.9.x for a
+version than 1.9, please first upgrade to moin 1.9.x. You may want to run 1.9.x for a
 while, so you can be sure everything is working as expected.
 
-Note: moin 1.9.x is a WSGI application, moin2 is also a WSGI application.
-So, upgrading to 1.9 first makes also sense concerning the WSGI / server side.
+Note: Both moin 1.9.x and moin2 are WSGI applications.
+Upgrading to 1.9 first also makes sense concerning the WSGI / server side.
 
 
 From moin 1.9.x
@@ -39,8 +39,8 @@
 
 Install moin2
 -------------
-Install and roughly configure moin2, make it work, start configuring it from
-the moin2 sample config (do not just use your 1.9 wikiconfig).
+Install and roughly configure moin2, make it work, and start configuring it from
+the moin2 sample config (do *not* just use your 1.9 wikiconfig).
 
 
 Adjusting the moin2 configuration
@@ -48,7 +48,7 @@
 It is essential that you adjust the wiki config before you import your 1.9
 data:
 
-Configuration::
+Example configuration::
 
     from os.path import join
     from MoinMoin.storage import create_simple_mapping
@@ -64,39 +64,39 @@
 Clean up your moin 1.9 data
 ---------------------------
 It is a good idea to clean up your 1.9 data first, before trying to import
-it into moin2. By getting the data into good shape you can avoid quite some
-warnings the importer would emit otherwise.
+it into moin2. In doing so you can avoid quite some
+warnings that the importer would otherwise produce.
 
-You do this with moin 1.9 (!), using these commands::
+You do this with moin *1.9* (!), using these commands::
 
   moin ... maint cleanpage
   moin ... maint cleancache
 
 .. todo::
-   add more infos about handling of deleted pages
+   add more info about handling of deleted pages
 
 
 Importing your moin 1.9 data
 ----------------------------
-It is assumed that you have no moin2 storage and no index created yet,
-thus we include -s and -i options to create the storage and an empty index.
+It is assumed that you have no moin2 storage and no index created yet.
+Thus, we include -s and -i options to create the storage and an empty index.
 
-The import19 will then read your 1.9 data_dir (pages, attachments and users),
-convert the data as needed and write it to your moin2 storage (and also
+The import19 argument to the `moin` script will then read your 1.9 data_dir (pages, attachments and users),
+convert the data as needed, and write it to your moin2 storage (and also
 build the index)::
 
   moin import19 -s -i --data_dir /your/moin/1.9/data 1>import1.log 2>import2.log
 
-If you use the command as given, it will write all output into 2 log files,
-please review them to find whether the importer had critical issues with your
+If you use the command as given, it will write all output into 2 log files.
+Please review them to find out whether the importer had critical issues with your
 data.
 
 
 Testing
 -------
-Just start moin now, it should have your data now.
+Just start moin now, as it should have your data available.
 
-Try "Index" and "History" views to see what's in there.
+Try "Index" and "History" views to see what's there.
 
 Check whether your data is complete and working OK.
 
@@ -107,6 +107,6 @@
 Keep your backups
 -----------------
 Make sure you keep all backups of your moin 1.9 installation (code, config,
-data), just for the case you are not happy with moin2 and need to go back for
+data), just in case you are not happy with moin2 and need to go back for
 some reason.
 
--- a/docs/conf.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/conf.py	Sat Jan 28 17:42:01 2012 +0100
@@ -97,6 +97,11 @@
 # a list of builtin themes.
 html_theme = 'default'
 
+# The style sheet to use for HTML pages. A file of that name must exist either
+# in Sphinx’ static/ path, or in one of the custom paths given in
+# html_static_path. Default is the stylesheet given by the selected theme.
+html_style = 'custom.css'
+
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
--- a/docs/devel/development.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/devel/development.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -60,8 +60,8 @@
 
 How MoinMoin works
 ==================
-This is just a very high level overview about how moin works, if you'ld like
-to know more details, you'll have to read more docs and the code.
+This is just a very high level overview about how moin works. If you would like
+to acquire a more in-depth understanding, try reading more docs and code.
 
 WSGI application creation
 -------------------------
@@ -69,7 +69,7 @@
 this will:
 
 * load the configuration (app.cfg)
-* register some Modules that handle different parts of the functionality
+* register some modules that handle different parts of the functionality
 
   - MoinMoin.apps.frontend - most stuff a normal user uses
   - MoinMoin.apps.admin - some stuff for admins
@@ -104,7 +104,7 @@
     + by fetching the item of name "WikiItem" from storage
     + it looks at the mimetype of this item (stored in metadata)
     + it creates an appropriately typed Item instance (depending on the mimetype)
-  - calls Item._render_data() to determine how the rendered item looks like
+  - calls Item._render_data() to determine what the rendered item looks like
     as HTML
   - renders the `show_item.html` template (and gives it the rendered item html)
   - returns the result to Flask
@@ -114,14 +114,14 @@
 Storage
 -------
 Moin supports different stores (like storing directly into files /
-directories, using key/value stores, using a SQL database, etc. - see
+directories, using key/value stores, using an SQL database, etc. - see
 `MoinMoin.storage.stores`). A store is extremly simple: just store a value
 for a key and retrieve the value using the key + iteration over keys.
 
-A backend is one layer above, dealing with objects that have metadata and
-data, see `MoinMoin.storage.backends`), still very simple stuff.
+A backend is one layer above. It deals with objects that have metadata and
+data (see `MoinMoin.storage.backends`) - still very simple stuff.
 
-Above that, there is misc. stuff in `MoinMoin.storage.middleware` for:
+Above that, there is miscellaneous stuff in `MoinMoin.storage.middleware` for:
 
 * routing by name to some specific backend (like fstab / mount)
 * indexing metadata and data + comfortable and fast index-based access,
@@ -130,19 +130,19 @@
 
 DOM based transformations
 -------------------------
-But how does moin know how the HTML rendering of some item looks like?
+But how does moin know what the HTML rendering of an item looks like?
 
 Each Item has some mimetype (stored in metadata) - the input mimetype.
 We also know what we want as output - the output mimetype.
 
 Moin uses converters to transform the input data into the output data in
-multiple steps and has a registry that knows all converters and their supported
+multiple steps. It also has a registry that knows all converters and their supported
 input and output mimetypes.
 
-For example, if the mimetype is `text/x-moin-wiki`, it'll find that the input
+For example, if the mimetype is `text/x-moin-wiki`, it will find that the input
 converter handling this is the one defined in `converter.moinwiki_in`. It then
 feeds the data of this item into this converter. The converter parses this
-input and creates a in-memory `dom tree` representation from it.
+input and creates an in-memory `dom tree` representation from it.
 
 This dom tree is then transformed through multiple dom-to-dom converters for
 e.g.:
@@ -155,18 +155,18 @@
 Finally, the dom-tree will reach the output converter, which will transform it
 into the desired output format, e.g. `text/html`.
 
-This is just one example of a supported transformation, there are quite a lot
-of converters in `MoinMoin.converter` supporting different input formats,
+This is just one example of a supported transformation. There are quite a few 
+converters in `MoinMoin.converter` supporting different input formats,
 dom-dom transformations and output formats.
 
 Templates and Themes
 --------------------
-Moin uses jinja2 as templating engine and Flask-Themes as a flask extension to
-support multiple themes (each themes has static data, like css, and templates).
+Moin uses jinja2 as its templating engine and Flask-Themes as a flask extension to
+support multiple themes (each theme has static data like css and templates).
 
 When rendering a template, the template is expanded within an environment of
-values it can use. Additionally to this (general) environment, parameters can
-be also given directly to the render call.
+values it can use. In addition to this (general) environment, parameters can
+also be given directly to the render call.
 
 Testing
 =======
@@ -176,11 +176,7 @@
 
 Running the tests
 -----------------
-To run the tests you first need to enter your virtualenv::
-
-    . env/bin/activate
-
-To run tests, enter::
+To run the tests, activate your virtual env and run::
 
     py.test  # runs all tests
     py.test -k somekeyword  # just run the tests matching somekeyword
@@ -188,7 +184,7 @@
 
 Tests output
 ------------
-Most is quite self-explaining, the characters mean::
+Most output is quite self-explanatory. The characters mean::
 
     . test ran OK
     s test was skipped
@@ -200,10 +196,10 @@
 
 Writing tests
 -------------
-Writing tests with `py.test` is easy and low on overhead. You basically just
+Writing tests with `py.test` is easy and has little overhead. You basically just
 use `assert` statements.
 
-For more information, please read on there: http://pytest.org/ - but keep in
+For more information, please read http://pytest.org/ - but keep in
 mind that we currently still use **py.test 1.3.4**.
 
 Documentation
@@ -214,8 +210,8 @@
 
 Creating docs
 -------------
-Sphinx can create all kinds of documentation formats, we'll just list the most
-popular ones below::
+Sphinx can create all kinds of documentation formats. The most
+popular ones are::
 
     cd docs
     make html  # create html docs (to browse online or in the filesystem)
--- a/docs/devel/support.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/devel/support.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -33,26 +33,26 @@
 * Even better: fix the bug, file a bug report and submit a patch (consider
   adding a unit test)
 
-Having an idea?
----------------
+Have an idea?
+-------------
 * Discuss it on IRC and file a feature request on the wiki.
 * Even better: discuss + write some Python code implementing it.
 
 Born to code?
 -------------
-* Help working on moin2 core, so it gets finished faster.
+* Help to work on moin2 core, so it gets finished faster.
 * Help maintaining moin 1.9 until moin2 is ready.
 
 No time, but money?
 -------------------
-* If you use moin in your business and you like some help from a moin
+* If you use moin in your business and you would like some help from a moin
   developer or administrator, consider buying some commercial support (see
   above). It'll save you some of your precious time, likely improve your
   moin experience and help moin administrators and developers to spend more
   time on moin.
 
-Having good language or documentation skills?
----------------------------------------------
+Have good language or documentation skills?
+-------------------------------------------
 * If you are a native speaker of a language other than English, with a good
   understanding of English, consider helping with improving translation to
   your language. **(not yet for moin2, too early!)** see also :doc:`translate`
@@ -60,4 +60,3 @@
   Here is a list of all TODOs in this documentation:
 
 .. todolist::
-
--- a/docs/devel/translate.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/devel/translate.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -5,15 +5,16 @@
 If your language already exists
 -------------------------------
 
-To find out if someone already started a translation of moin2 into your
-language, check the folder MoinMoin/translations in the source tree.
+To find out if someone has already started a translation of moin2 into your
+language; check the folder MoinMoin/translations in the source tree.
 If there is a folder with your language code (locale) [#]_, you can just
 start with the steps below. If not, please take a look at `If your
 language doesn't exist yet`_.
 
 
 1. Make sure you have the latest version of the source tree (hg).
-   You will also need to have python installed.
+   You will also need to have python installed, with setuptools and babel
+   packages.
 
 2. Go to the top directory and execute::
 
@@ -29,7 +30,7 @@
    * find an entry, with an empty or bad translated text (the text after
      msgstr) and do your changes.
    
-   * **never** edit the msgid string, just edit the msgstr field
+   * **never** edit the 'msgid' string, just edit the 'msgstr' field
    
    * Variables like ``%(name)x`` (x can be any character) must be kept as
      they are. They must occur in the translated text.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/examples/deployment/moin-http-basic-auth.conf	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,63 @@
+# vim: ts=4 filetype=apache
+
+## Sample vhost config file for Apache 2 and MoinMoin with HTTP basic auth
+
+## Add `Include /path/to/this/file` in your Apache httpd.conf to activate
+## this configuration (or place this file in the location specified by your
+## distribution), and remember to install mod_wsgi and start Apache
+## with "-D WSGI" if mod_wsgi is not already enabled
+
+## For more information on mod_wsgi configuration directives, see
+## https://code.google.com/p/modwsgi/wiki/ConfigurationDirectives
+
+## Listen to port 80 on all IPs
+<VirtualHost *:80>
+    ## Replace with the name of your server (eg. localhost or my.domain.tld)
+    ServerName myservername
+
+    ## Note that python-path is required when deploying using virtualenv so
+    ## that the interpreter can find the necessary Python packages installed
+    ## with MoinMoin
+    WSGIDaemonProcess moinmoin-wsgi user=myuser
+        python-path=/path/to/moin/moin/virtualenv/python/interpreter/site-packages
+
+    ## The wiki will be accessible from myservername:80/wiki/root/url/ now
+    WSGIScriptAlias /wiki/root/url /path/to/moin/moin/install/moinmoin.wsgi
+
+    ## Let WSGI applications use HTTP authentication methods
+    WSGIPassAuthorization On
+
+    <Directory /path/to/moin/moin/install>
+        ## HTTP basic auth
+        ## Works with MoinMoin's GivenAuth authentication backend
+        ## See https://httpd.apache.org/docs/2.0/howto/auth.html for more
+        ## information on Apache authentication
+        AuthType Basic
+
+        ## Authname is the message that the browser will display when asking
+        ## for username and password
+        AuthName "Please log in"
+
+        ## AuthUserFile contains usernames and passwords for wiki users
+        ## and can be generated using the htpasswd utility included with
+        ## Apache
+        AuthUserFile /path/to/moin/apache/passwords/file
+
+        ## valid-user means that the supplied username must be in AuthUserFile
+        ## and the supplied password must match the entry for the user in
+        ## AuthUserFile
+        Require valid-user
+
+        ## Name of the WSGI processes
+        WSGIProcessGroup moinmoin-wsgi
+
+        ## All WSGI applications in group %{GLOBAL} are run under the
+        ## same interpreter for compatibility with the Global Interpreter
+        ## Lock when using some C extensions
+        WSGIApplicationGroup %{GLOBAL}
+
+        Order deny,allow
+        Allow from all
+    </Directory>
+</VirtualHost>
+
--- a/docs/examples/deployment/test.wsgi	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/examples/deployment/test.wsgi	Sat Jan 28 17:42:01 2012 +0100
@@ -16,13 +16,15 @@
 
 If you start this script from the commandline, it will serve the content on
 http://localhost:8080/ - this is mainly for debugging THIS script.
-    
+
 If you use this script with Apache2 and mod-wsgi, add those statements to your
 Apache's VirtualHost definition:
-    
+
     # you will invoke this test script at the root url, like http://servername/:
     WSGIScriptAlias / /some/path/test.wsgi
 
+    # Windows users stop here: WSGIDaemonProcess and WSGIProcessGroup are not supported on Windows hosts
+
     # create some wsgi daemons - use someuser.somegroup same as your data_dir:
     WSGIDaemonProcess test-wsgi user=someuser group=somegroup processes=5 threads=10 maximum-requests=1000 umask=0007
 
--- a/docs/index.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/index.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -1,9 +1,9 @@
 .. warning::
 
    This documentation **only** applies to **MoinMoin version 2** (aka moin2,
-   moin 2.0, mm2, MoinMoin2, etc.), except where explicitely noted otherwise.
-   moin2 is very different from moin 1.x, so neither old docs will apply to
-   new moin, nor vice versa.
+   moin 2.0, mm2, MoinMoin2, etc.), except where explicitly noted otherwise.
+   Moin2 is very different from moin 1.x, so docs from one version will not 
+   apply to the other.
 
 Introducing MoinMoin
 ====================
@@ -20,6 +20,7 @@
 .. toctree::
    :maxdepth: 2
 
+   user/accounts
    user/searching
    user/markups
    user/search
@@ -66,7 +67,7 @@
    devel/api/MoinMoin
 
 
-Indices and tables
+Indices and Tables
 ==================
 
 * :ref:`genindex`
--- a/docs/intro/features.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/intro/features.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -2,15 +2,15 @@
 Features
 ========
 We recommend trying out moin rather than reading feature lists.
-But in case you need it nevertheless, here it is:
+But in case you still need one, here it is:
 
 Operating System Support
 ========================
-Moin is implemented in Python and that is a platform independant language.
-So it'll work on Linux, Mac OS X, Windows (and others).
+Moin is implemented in Python, a platform-independant language.
+It works on Linux, Mac OS X, Windows (among other operating systems).
 
 That said, Linux is the preferred and most tested deployment platform and
-likely having less issues than e.g. Windows.
+will likely have fewer issues than, for example, Windows.
 
 Servers
 =======
@@ -19,7 +19,7 @@
 
   - Apache2 with mod_wsgi
   - IIS with isapi-wsgi (not recommended - if you must use Windows, but have
-    a choice concerning the web server, please rather use Apache2).
+    a choice concerning the web server, please use Apache2).
   - Other WSGI servers, see http://wsgi.org/
 
 * With the help of flup middleware about any other server speaking:
@@ -45,8 +45,8 @@
   - local (per wiki item)
   - give rights like:
 
-    + create,destroy
-    + read,write,rename
+    + create, destroy
+    + read, write, rename
     + admin
 
   - to:
@@ -86,8 +86,8 @@
 
 Search / Indexing
 =================
-* important metadata is put into index
-* content data is converted and put into index
+* important metadata is indexed
+* content data is converted and indexed
 * fast indexed search, fast internal operations
 * flexible and powerful search queries
 * search current and historical contents
@@ -107,7 +107,7 @@
 Wiki features
 -------------
 * Global History for all items (full list)
-* Latest Changes ("Recent Changes"), only lists latest changes of an item
+* Latest Changes ("Recent Changes"), only lists the latest changes of an item
 * Local History for one item ("History")
 * Diffs between any revision
 
@@ -143,8 +143,8 @@
 
 Translation / Localization
 --------------------------
-* currently English and German translations only (this is intended to stay like
-  that until the code and the texts are more stable)
+* currently English and German translations only (no others will be added until
+  the code and texts are more stable)
 * any localization (provided by babel / pytz)
 
 Logging
--- a/docs/intro/general.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/intro/general.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -9,12 +9,12 @@
 
 Project homepage: http://moinmo.in/
 
-Using MoinMoin, wiki users can easily create and update web content by just
-using a web browser.
+Using MoinMoin, wiki users can easily create and maintain web content from 
+their browser.
 
 You can use it:
 
-* as an easy maintainable web site
+* as an easily-maintained web site
 * as a knowledge base
 * for taking notes
 * for creating documentation
@@ -22,9 +22,9 @@
 You can use it for:
 
 * your company / organisation, your work group
-* your school, college or university
+* your school, college, or university
 * your projects and interests
-* just for yourself
+* just yourself
 
 You can run it on:
 
@@ -37,42 +37,42 @@
 What makes MoinMoin special?
 ----------------------------
 Moin tries to be a **great wiki engine** (good, powerful, extendable and still
-easy-to-use). We don't try to be everything else also, we don't try to be
+easy-to-use). We don't try to be everything, but we don't try to be
 minimalistic either.
 
-There are lots of wiki engines out there and it is hard to choose.
-But choosing wisely is a good idea, because you'll have to live with your
-choice for a longer time (switching wiki engines is not easy).
+There are lots of wiki engines out there, making it hard to pick one.
+However, choosing wisely is important because you may have to live with 
+your choice for a long time (switching wiki engines is not easy).
 
-We won't list all moin features right here, because comparing feature lists
-is just not enough. For some "features" it is even better if one does *not*
-implement them, even if they sound great at first. Also, you will find most
+We won't list all of moin's features right here, because comparing feature lists
+is just not enough. Some features are best left unimplemented, 
+even if they sound great at first. In moin, you will find most
 important features in most major wiki engines - but still, you and your wiki
 users might feel quite a different overall experience just because of a bunch
-of tiny unsuspiciously looking differences. Also, of course the quality of
-implementation of some feature can vary in a wide range. Thus, you have to
-try it, play with it, not just look at feature comparisons.
+of small, superficial differences. Of course the quality of some features'
+implementations can vary greatly. Thus, you have to
+try it and play with it, not just look at feature comparisons.
 
-MoinMoin has **been there since about year 2000**.
-Until moin 1.9.x it has quite rapidly grown and evolved, its developers have
-also grown their experience with Python and wiki technology over the years.
-With **moin 2.0**, based on that experience, there has been a rather
-**revolutionary cleanup / rewrite** of how moin works - to make it easier,
-cleaner, better, more consistent, more powerful, more flexible and more
+MoinMoin has **been around since about 2000**.
+It has rapidly grown and evolved through moin 1.9.x. Its developers have
+increased their experience with Python and wiki technology over the years.
+With **moin 2.0**, there has been a rather **revolutionary cleanup / rewrite** 
+of how moin works based on that experience. This promises to make it easier,
+cleaner, better, more consistent, more powerful, more flexible, and more
 modular.
 
-Moin is **written in Python** (an easy to read, high-level, object-oriented,
-dynamic, well-designed and platform-independent programming language) - it
-is fun to write clean code with Python.
+Moin is **written in Python** - an easy to read, high-level, object-oriented,
+dynamic, well-designed, and platform-independent programming language. It's 
+fun to write clean code with Python!
 
-Even if you are not a developer, but just a wiki admin or user, you indirectly
-take advantage of this (believe it or not, software developers are humans,
-too - if they use a crap programming language that is hard to read, badly
-designed and requiring them to write big amounts of code even for trivial
-things, it is hard for them to create and maintain a great software based on
-that).
+Even if you are but a mere wiki admin or user, you indirectly
+take advantage of this. Believe it or not, software developers are humans,
+too; if they use a crap programming language that is hard to read, badly
+designed, and requires them to write big amounts of code for even trivial
+things, then it is hard for them to create and maintain a great piece of 
+software.
 
-And you even could learn a bit of Python and extend moin as you like!
+You even could learn a bit of Python and extend moin as you like!
 
 Moin is **Free Software** (that implies that it is **Open Source**) and,
 because we use Python, you may even *like* to read and modify moin's code.
@@ -80,10 +80,10 @@
 
 Who is using MoinMoin?
 ----------------------
-This is just showing some more well-known users of MoinMoin and by no means
-a complete list.
+This just shows some of the better-known users of MoinMoin and is by no 
+means a complete list.
 
-Internet sites
+Web Sites
 ~~~~~~~~~~~~~~
 * KernelNewbies, Xen, LinuxWireless, GCC
 * Debian, Ubuntu, CentOS
@@ -100,7 +100,7 @@
 
 Intranet installations
 ~~~~~~~~~~~~~~~~~~~~~~
-We also know that there are a lot of non-public intranet installations of
+We know that there are a lot of private intranet installations of
 MoinMoin in:
 
 * enterprises, companies
@@ -108,6 +108,5 @@
 * scientific research facilities, universities, schools
 * communities
   
-But, we can't name them here due to legal reasons (we would need their explicit
-allowance to do so).
+Unfortunately, we do not have permission to name them here.
 
--- a/docs/intro/glossary.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/intro/glossary.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -14,8 +14,8 @@
 
    contenttype
       A formal, standardized way of specifying of which type some data is.
-      E.g. 'text/plain;charset=utf-8' is contenttype for some simple piece
-      of text (encoded using utf-8 encoding), 'image/png' is contenttype
+      E.g. 'text/plain;charset=utf-8' is the contenttype for some simple piece
+      of text (encoded using utf-8 encoding), 'image/png' is the contenttype
       for a PNG image.
 
    item
@@ -28,12 +28,12 @@
       revision.
 
    data
-      Just the raw data, no more, no less (can be some text, a image, a pdf).
+      Just the raw data, no more, no less (can be some text, an image, a pdf).
 
    metadata
       Additional information related to or about some data. For example, if
       you create a new PDF item revision, the revision data will be the PDF
-      file's content, but moin will also additionally store revision meta data
+      file's content, but moin will also additionally store revision metadata
       that tells that this revision is in fact a PDF (its contenttype - we do not
       rely on or require .pdf extension on the item name), when it was saved,
       maybe some comment you gave when saving, etc.
@@ -46,12 +46,12 @@
       navigation).
 
    wiki engine
-      A software used to run a wiki site.
+      Software used to run a wiki site.
 
    wiki farm
       Running multiple wikis together on one server. Often, there is some
-      shared, common configuration used (inherited) by all wikis, so that
-      the individual wiki's configuration becomes rather small.
+      shared, common configuration used (inherited) by all wikis, so each
+      individual wiki's configuration becomes rather small.
 
    wiki instance
       All configuration and data related to a single wiki.
@@ -60,7 +60,7 @@
       A single content item within a wiki site.
 
    wiki page
-      A single content item within a wiki site, maybe rather used for text-like items.
+      A single content item within a wiki site, possibly used for text-like items.
 
    wiki site
       A web site implemented using a wiki engine.
@@ -71,7 +71,7 @@
       e.g. MoinMoin). It is a Python standard, described in detail in PEP 333.
 
    emeraldtree
-      A xml / tree processing library used by moin.
+      An XML / tree processing library used by moin.
 
    flask
       A micro framework used by moin.
@@ -80,7 +80,7 @@
       A templating engine used by moin.
 
    sqlalchemy
-      A SQL database abstraction library used by moin.
+      An SQL database abstraction library used by moin.
 
    sqlite
       An easy-to-use SQL database used by moin.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/user/accounts.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -0,0 +1,237 @@
+=============
+User Accounts
+=============
+
+Accounts provide an easy way for wiki users to identify themselves to MoinMoin and other wiki users, 
+store personal preferences and track wiki contributions. Account creation is simple and 
+straightforward, and provides many benefits over browsing and editing anonymously.
+
+Account Creation
+================
+
+To create an account, click the :guilabel:`Login` button at the top of the page. You will be taken to a login
+page allowing you to either log in or create an account. Proceed to the create account page
+by clicking the account creation button, and you will be presented with an account creation form.
+The fields of this form are as follows:
+
+Name
+ Your username on the wiki. Will appear in the history section of any wiki item which you edit. This is a required field.
+
+Password
+ Your password for logging into your new account. Remember to pick a strong password with a mix
+ of upper and lower case letters, numbers and symbols. This is also a required field.
+
+Password
+ Enter your new password again (same as the above field). This is a required field to make sure
+ that your first password entry was correct.
+
+E-Mail
+ The email address which will be associated with your new account. This can be used by the wiki
+ administrators to contact you or to verify your account if email verification is enabled on
+ the wiki. This is a required field.
+
+OpenID
+ This is an optional field where you may enter an OpenID to be associated with your account. OpenID
+ provides a common mechanism for websites to authenticate users and store data about them like
+ username and real name. If you have an OpenID, you may want to enter it here.
+
+.. note::
+ Some wikis require email verification, in which case you will have click an activation link which
+ will be sent to the email address you provide. You must complete this step before you start using
+ the wiki.
+
+User Settings
+=============
+
+User settings provide a way for to customise your MoinMoin experience and perform account
+maintenance functions like changing email address or password. To access your settings page, click
+the :guilabel:`Settings` button at the top of the page.
+
+The settings page appears as a list of links to various sub-pages for changing elements of your
+wiki experience, each of these sub-pages are listed below:
+
+Personal Settings
+-----------------
+
+Personal settings include wiki language and locale, username, alias and OpenID.
+
+Name
+ Your username, as it will appear on the wiki and in the history pages of wiki items which you edit.
+
+Alias-Name
+ The alias name can be used to override your username, so you will still log in using your username
+ but your alias name will be displayed to other users and in your history page.
+
+OpenID
+ If you have an OpenID which you would like to associate with your account, enter it here.
+
+.. warning::
+ **MOINTODO** Leaving the OpenID field blank gives a warning saying "This OpenID is already in use"
+
+Timezone
+ Setting this value will allow you to see edit times as they would appear in your time zone. For
+ example, an edit time of 10AM UTC would appear as 8PM AEST if you changed your time zone to 
+ GMT +10/Australian Eastern Standard Time.
+
+Locale
+ Your preferred language for interacting with MoinMoin.
+
+Change Password
+---------------
+
+Password changes are recommended if you believe that the password you are using has been put into
+the hands of an untrusted third party.
+
+Current Password
+ Enter the password which you currently use to log into the wiki. This prevents passersby from
+ changing the password of a logged in account. This is a required field.
+
+New Password
+ The new password which you would like to use. This is a required field.
+
+New Password (repeat)
+ Enter your new password again. Used to detect typographical errors. This is a required field.
+
+Notification Settings
+---------------------
+
+Notification settings allow you to configure the way MoinMoin notifies you of changes and important
+information.
+
+E-Mail
+ Change the email address MoinMoin sends emails to.
+
+Wiki Appearance Settings
+------------------------
+
+Appearance settings allow you to customise the look and feel of the wiki.
+
+Theme name
+ The bundled MoinMoin wiki theme which you would like to use.
+
+User CSS URL
+ If you want to style MoinMoin with custom Cascading Style Sheets (CSS), enter a URL for your
+ custom stylesheet here. Custom CSS provides an advanced level of control over appearance of
+ MoinMoin pages.
+
+Editor Size
+ The size (in lines) of MoinMoin's plain text editor when you edit an item.
+
+.. warning::
+ **MOINTODO** "Editor Size" isn't a very good title as it doesn't specify *which* editor or in what 
+ units the size is. This setting doesn't seem to affect my MoinMoin instance, either.
+
+History results per page
+ The number of edits you will see when you look at the history of an item.
+
+Navigation Settings
+-------------------
+
+.. warning::
+ **MOINTODO** This page is blank. Perhaps it should be removed?
+
+Options
+-------
+
+.. warning::
+ **MOINTODO** "Options" isn't a very good name. Aren't they all "options"? The settings in the
+ options page don't seem to be grouped in any particular category, either. Perhaps these options
+ should be moved to another settings page?
+
+The "Options" section allows you to control privacy and advanced features of MoinMoin.
+
+Publish my email (not my wiki homepage) in author info
+ Control whether or not other wiki users may see your email address.
+
+Open editor on double click
+ This option allows you to simply double click the text on any MoinMoin item and have it opened
+ in the editor.
+
+Show comment sections
+ Show the comment sections for wiki items you view.
+
+Disable this account forever
+ Tick this box if you want to disable your account. Your username or alias will still show in the
+ history pages of items you have edited, but you will no longer be able to log in using your
+ account.
+
+Special Features for Users with Accounts
+========================================
+
+Your User Page
+--------------
+
+.. warning::
+ **MOINTODO** User pages are currently broken as they have no handler for their content type. Because
+ of this, they use the binary content type and cannot be viewed (they must be downloaded).
+
+You user page is a wiki space in which you may share information about yourself with other users of
+that wiki. It can be accessed by clicking the button with your username on it at the top of the
+screen, and is edited like a normal wiki item.
+
+"My Changes"
+------------
+
+To view your modifications to a wiki, navigate to the URL ``/+mychanges``. This will show a list of
+modifications you have made to wiki items.
+
+.. warning::
+ **MOINTODO** There is currently no button to get to +mychanges. This should be fixed.
+
+ **MOINTODO** +mychanges only links to the item which you edit, not the specific revision. If you edit
+ and item several times, it just inserts several identical links to that item. This behaviour should be
+ checked and rectified.
+
+ **MOINTODO** +mychanges isn't very pretty if you visit it without making any changes, it just says 
+ "My Changes" at the top with the rest of the page left blank.
+
+Bookmarking
+-----------
+
+Some MoinMoin users spend a lot of time sifting through the global changes list (accessible via the
+:guilabel:`History` button at the top of every MoinMoin page) looking for unread changes.
+To help users remember which revisions they have read and which they have yet to read,
+MoinMoin provides bookmarks. If you have read revisions up until the 13th of January, for example, you would
+simply click the :guilabel:`Set bookmark` button next to the revisions from the 13th of January to hide
+all revisions from before that date. If you wish to examine those revisions again, navigate back to the 
+global history page and click :guilabel:`Remove bookmark`.
+
+Quicklinks
+----------
+
+At the top of every MoinMoin page, there is a row of buttons for quick access to commonly used MoinMoin
+features like the global index, global history and homepage. Often, users need quick access to MoinMoin
+items without having to search for them each time - quicklinks allow you to access your favourite wiki
+items at the click of a button by placing links to them at the top of every page. To quicklink an item,
+click the :guilabel:`Add Link` button at the top or bottom of a MoinMoin item. To remove a quicklink,
+simply navigate back to the item and click the :guilabel:`Remove Link` button.
+
+Quicklinks are associated with your account, so you will be able to access them from anywhere by simply
+logging into the wiki.
+
+Item Trail
+----------
+
+The item trail appears at the top of each page and lists previous items which you have visited. Users
+with accounts may view this trail wherever they log in, whereas anonymous users have a different trail
+on each computer that they visit.
+
+Subscribing to Items
+--------------------
+
+Subscribing to items allows you to be notified via email when changes are made. To subscribe, navigate
+to the item in question and click the :guilabel:`Subscribe` button at the top or bottom of the page. You 
+will now receive an email each time a user modifies this item. To unsubscribe, navigate to the item
+again and click the :guilabel:`Unsubscribe` button at the top or bottom of the page.
+
+Logging out
+===========
+
+.. warning::
+ **MOINTODO** Currently logging out just removes the user's session cookie. These cookies remain
+ valid after logging out (and even after a password change), and could be used to impersonate a
+ user. See BitBucket issue #94.
+
+Logging out of your account can prevent account hijacking on untrusted or insecure computers, and is
+considered best practice for security. To log out, click the :guilabel:`Logout` button at the top
+of the page. You will be redirected to a page confirming that you have logged out successfully.
--- a/docs/user/creolewiki.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/user/creolewiki.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -8,8 +8,6 @@
 
 Features currently not working with moin's rst parser are marked with **RSTTODO**.
 
-Features currently not working with moin's sphinx setup are marked with **SPHINXTODO**.
-
 Headings
 ========
 
@@ -27,7 +25,7 @@
 Level 1
 =======
 
-**Intentionally not rendered as level 1 so as to not interfere with Sphinx's indexing**
+**Intentionally not rendered as level 1 so it does not interfere with Sphinx's indexing**
 
 Level 2
 =======
@@ -46,13 +44,13 @@
 
 **Notes**:
 
-Closing equals signs are optional, and if they are used they will not affect the output.
+Closing equals signs are optional and do not affect the output.
 Also, whitespace between the first word of the heading and the opening equals sign will not be shown in the output (ie. leading whitespace is stripped).
 
 Text formatting
 ===============
 
-The following is a table of inline markup that can be used to control text formatting in Creole.
+The following is a table of inline markup that can be used to format text in Creole.
 
 +-------------------------------------+---------------------------------------+
 | Markup                              | Result                                |
@@ -67,14 +65,7 @@
 |                                     | | Second line                         | 
 +-------------------------------------+---------------------------------------+
 
-**SPHINXTODO** **RSTTODO**: Restructured Text does not allow text to be both **bold** and *italic*. This is because bold and italic are simply
-treated as different levels of emphasis. It should be noted that this is a problem with the spec rather than Sphinx or Moin itself.
-
-It requires the following CSS to rectify: ::
-
-   .bolditalic{font-weight:bold;font-style:italic;}
-
-**RSTTODO**: reStructuredText line blocks are not working in Moin2
+**RSTTODO**: Restructured Text line blocks are not working in Moin2
 
 Hyperlinks
 ==========
@@ -248,7 +239,7 @@
 
     spam()
 
-**CREOLETODO**: Use of syntax highlighting currently crashes moin.
+**CREOLETODO**:The use of syntax highlighting currently crashes moin.
 
 Lists
 =====
@@ -257,7 +248,7 @@
 -------------
 
 Ordered lists are formed of lines that start with number signs (``#``).
-The count of number signs at the beginning of a line determines the level.
+The number of '#' signs at the beginning of a line determines the current level.
 
 **Markup**: ::
 
@@ -347,7 +338,7 @@
 **Notes**:
 
 Table cells start with a pipe symbol (``|``), and header cells start with a pipe symbol and equals sign (``|=``).
-The closing pipe symbol at the end of a row is completely optional.
+The closing pipe symbol at the end of a row is optional.
 
 **CREOLETODO** **RSTTODO**: Table headers are not interpreted as such when rendered.
 
--- a/docs/user/markups.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/user/markups.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -17,7 +17,7 @@
 .. _Docbook: http://www.docbook.org/ 
 .. _MediaWiki: http://www.mediawiki.org/wiki/Help:Formatting
 
-In Moin2, you specify an item's markup language when you create the document. An item's markup language can also be changed at any time by modifying the item's ``mimetype`` metadata. Currently Moin2 supports `MoinWiki`_, `WikiCreole`_, `reStructuredText`_, `Docbook`_ and `MediaWiki`_ markups.
+In Moin2, you specify the item's markup language when you create the document. Its markup language can also be changed at any time by modifying the item's ``mimetype`` metadata. Currently Moin2 supports `MoinWiki`_, `WikiCreole`_, `reStructuredText`_, `Docbook`_ and `MediaWiki`_ markups.
 
 **MOINTODO**: Currently the use of ``{{{#!syntax content}}}`` parsers crashes moin. This should be looked into.
 
--- a/docs/user/mediawiki.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/user/mediawiki.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -10,8 +10,6 @@
 
 Features currently not working with moin's rst parser are marked with **RSTTODO**.
 
-Features currently not working with moin's sphinx setup are marked with **SPHINXTODO**.
-
 Headings
 ========
 
@@ -29,7 +27,7 @@
 Level 1
 =======
 
-**Intentionally not rendered as level 1 so as to not interfere with Sphinx's indexing**
+**Intentionally not rendered as level 1 so it does not interfere with Sphinx's indexing**
 
 Level 2
 =======
@@ -49,7 +47,7 @@
 Text formatting
 ===============
 
-These markups can be used within text to apply character styles.
+These markups can be used within text to apply character style.
 
 +------------------------------------+------------------------------------+
 | Markup                             | Result                             |
@@ -76,13 +74,6 @@
 | | ``without '''markups'''</pre>``  | | ``without '''markups'''``        |
 +------------------------------------+------------------------------------+
 
-**SPHINXTODO**
-following css is needed to display bold&italic, underline and strikethrough correctly: ::
-
- span.underline { text-decoration: underline; }
- span.strikethrough { text-decoration: line-through; }
- span.bolditalic { font-weight: bold; font-style: italic; }
-
 **RSTTODO**
 table headers are not formatted as headers
 (see "Tables" section for corresponding MWTODO)
--- a/docs/user/moinwiki.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/user/moinwiki.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -8,16 +8,11 @@
 Moin Wiki markup overview
 ==========================
 
-The report follows moin 1.9 help page and reports syntaxes that do not verify 1.9 help syntax documentation.
+The report follows the moin 1.9 help page and reports syntaxes that do not match 1.9 help syntax documentation.
 The structure and order has been matched with other markup rst files namely creoleWiki.rst and mediaWiki.rst at http://hg.moinmo.in/moin/2.0-dev/file/42d8cde592fb/docs/user
 
 Features currently not working with moin's Wiki parser are marked with **MOINTODO**.
 
-Features currently not working with moin's sphinx setup are marked with **SPHINXTODO**.
-
-
-**SPHINXTODO CSS**, the tables seem to have missing borders despite of the fact that the rst markup is correct.
-
 Table Of Contents
 =================
 
@@ -96,9 +91,6 @@
 | ``--(Stroke)--``                    | :strikethrough:`Stroke`               |
 +-------------------------------------+---------------------------------------+
 
-**Notes**:
- - **SPHINXTODO** Superscript, subscript and underline are not working in rst.
-
 Hyperlinks
 ==========
 
@@ -189,6 +181,12 @@
 Lists
 =====
 
+.. warning::
+   All Moin Wiki list syntax (including that for unordered lists, ordered lists and definition lists) requires a leading space before each item in the list.
+   Unfortunately, reStructuredText does not allow leading whitespace in code samples, so the example markup here will not work if copied verbatim, and requires
+   that each line of the list be indented by one space in order to be valid Moin Wiki markup.
+   This is also an **RSTTODO**
+
 Unordered Lists
 ---------------
 
@@ -290,11 +288,6 @@
    
  B. item 2
    
-**Notes**:
- - **SPHINXTODO** sphinx will remove the first space before every list item.
- - Moin increases the order number/roman/letter automaticaly. rst does not do any such thing, so i have to manually increase them here.
- - even the base level item has to have a space in the begining
-
 Definition Lists
 ================
 
@@ -310,8 +303,12 @@
  term
   definition
  object
-  description 1
-  description 2
+  | description 1
+  | description 2
+
+**Notes**:
+ - reStructuredText does not support multiple definitions for a single term, so a line break has been forced to illustrate the appearance of several definitions.
+   Using the prescribed Moin Wiki markup will, in fact, produce two separate definitions in MoinMoin (using separate ``<dd>`` tags).
   
 Tables
 ======
@@ -347,7 +344,7 @@
 
 **Notes**:
  - **MOINTODO:** the cell width does not work in moin 2.
- - **SPHINXTODO** rst does not support percentage cell width so cell has been made long manually
+ - reStructuredText does not support percentage cell width so cell has been made long manually. In MoinMoin the second cell will take up the maximum amount of horizontal space.
 
 Spanning Rows and Columns
 -------------------------
@@ -394,7 +391,7 @@
 +----------------+---------------------------------------+-------------------+
 
 **Notes**:
- - **SPHINXTODO** bottom align cannot be shown in rst.
+ - Text cannot be aligned in reStructuredText, but the text will appear as is described when used in MoinMoin.
 
 HTML-like Options for Tables
 ----------------------------
@@ -478,13 +475,15 @@
     print "Hello World!"
  }}}
  
-**Result**: ::
+**Result**:
 
- ---
- 
+.. code-block:: python
+
+    def hello():
+        print "Hello, world!"
+
 **Notes**:
  - The syntax crashes moin2.
- - **SPHINXTODO** The html required for the syntax box cannot be shown in rst.
 
 Using the wiki parser with css classes
 --------------------------------------
@@ -502,7 +501,7 @@
 +----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 
 **Notes**:
- - **SPHINXTODO** The div cannot be shown in rst, so a table cell has been made to imitate it.
+ - The div cannot be shown in reStructuredText, so a table cell has been made to demonstrate the border produced. In MoinMoin, this border will appear red.
 
 Admonitions
 -----------
@@ -517,10 +516,10 @@
  
 **Result**:
 
----
+.. warning::
+    **Don't overuse admonitions**
 
-**Notes**:
- - **SPHINXTODO** The Admonition cannot be shown in rst.
+    Admonitions should be used with care. A page riddled with admonitions will look restless and will be harder to follow than a page where admonitions are used sparingly.
 
 Comments
 --------
@@ -535,8 +534,12 @@
  
 **Result**:
 
----
++--------------------------------------------------------------------------------+
+| This is a wiki parser section with class "comment dotted" (see HelpOnParsers). |
+|                                                                                |
+| Its visibility gets toggled the same way.                                      |
++--------------------------------------------------------------------------------+
 
 **Notes**:
- - **SPHINXTODO** The wiki parser section with class "comment dotted" cannot be shown in rst.
+ - reStructuredText has no support for dotted borders, so a table cell is used to illustrate the border which will be produced. This markup will actually produce a dotted border in MoinMoin.
  - The toggle display feature does not work yet
--- a/docs/user/rest.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/user/rest.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -2,15 +2,10 @@
 ReST (ReStructured Text) Markup
 ===============================
 
-The report gives reST syntax documentation. The structure and order has been matched with other markup rst files namely creolewiki.rst and mediawiki.rst at http://hg.moinmo.in/moin/2.0-dev/file/42d8cde592fb/docs/user
+The report gives reST syntax documentation. The structure and order has been matched with the other markup rst files namely creolewiki.rst and mediawiki.rst at http://hg.moinmo.in/moin/2.0-dev/file/42d8cde592fb/docs/user
 
 Features currently not working with moin's Wiki parser are marked with **RSTTODO**.
 
-Features currently not working with moin's sphinx setup are marked with **SPHINXTODO**.
-
-
-**SPHINXTODO CSS**, the tables seem to have missing borders despite of the fact that the rst markup is correct.
-
 Headings
 ========
 
@@ -22,7 +17,7 @@
  Level 1
  =======
 
- **Intentionally not rendered as level 1 so as to not interfere with Sphinx's indexing**
+ **Intentionally not rendered as level 1 so it doesn't interfere with Sphinx's indexing**
 
  Level 2
  =======
@@ -44,7 +39,7 @@
 Level 1
 =======
 
-**Intentionally not rendered as level 1 so as to not interfere with Sphinx's indexing**
+**Intentionally not rendered as level 1 so it doesn't interfere with Sphinx's indexing**
 
 Level 2
 =======
@@ -66,7 +61,7 @@
 Text formatting
 ===============
 
-The following is a table of inline markup that can be used to control text formatting in Moin.
+The following is a table of inline markup that can be used to format text in Moin.
 
 +-------------------------------------+---------------------------------------+
 | Markup                              | Result                                |
@@ -142,12 +137,12 @@
 **Markup**: ::
 
  indented text
-  text indented to the 2nd level
+  text indented for the 2nd level
 
 **Result**:
 
  indented text
-  text indented to the 2nd level
+  text indented for the 2nd level
 
 **Markup**: ::
 
@@ -206,26 +201,26 @@
 
  1. item 1
  
-   1. item 1.1
+    1. item 1.1
+    #. item 1.2
    
-   2. item 1.2
-   
- 2. item 2
+ #. item 2
 
 **Result**:
 
  1. item 1
  
-   1. item 1.1
+    1. item 1.1
+    #. item 1.2
    
-   2. item 1.2
-   
- 2. item 2
+ #. item 2
    
 **Notes**:
- - The order and the numbering agent have to be maintained by user. Any thing can be used to number the items (e.g. a/A or i/I).
- - **SPHINXTODO** sphinx will remove the first space before every list item.
- - even the base level item has to have a space in the beginning
+ - Ordered lists can be automatically enumerated using the ``#`` character as demonstrated above. Note that the first item of an ordered list
+   auto-enumerated in this fashion must use explicit numbering notation (e.g. ``1.``) in order to select the enumeration sequence type
+   (e.g. Roman numerals, Arabic numerals, etc.), initial number (for lists which do not start at "1") and formatting type (e.g. ``1.`` or ``(1)`` or ``1)``). More information on
+   enumerated lists can be found in the `reStructuredText documentation <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#enumerated-lists>`_.
+ - One or more blank lines are required before and after reStructuredText lists.
 
 Definition Lists
 ================
@@ -342,7 +337,7 @@
 Comments
 ========
 
-Comments are not shown on the page but depending on the output formatter, they might be included as HTML comments (``<!-- -->``).
+Comments are not shown on the page but depending on the output formatter they might be included as HTML comments (``<!-- -->``).
 
 **Markup**: ::
  
@@ -373,7 +368,7 @@
 Literals Blocks
 ===============
 
-Literal blocks are used to show test as-is. i.e no markup processing is done within a literal block. A minimum (1) indentation is required for the text block to be recognized as a literal block.
+Literal blocks are used to show test as-it-is. i.e no markup processing is done within a literal block. A minimum (1) indentation is required for the text block to be recognized as a literal block.
 
 **Markup**: ::
 
--- a/docs/user/search.rst	Tue Nov 08 03:23:16 2011 +0100
+++ b/docs/user/search.rst	Sat Jan 28 17:42:01 2012 +0100
@@ -5,21 +5,21 @@
 Entering search queries
 =======================
 
-Usually there is a simple and rather short search query input field offered by
-the theme - if you submit a query from there, it will search in item names and
+Usually there is a simple but rather short search query input field offered by
+the theme - by submiting a query it will search in item names and
 content (but only in the current stuff, not in non-current revisions) and display
 the search results to you.
 
-On that search results view, you will get a bigger search query input field
+On the search results view you will get a bigger search query input field
 (e.g. for refining your query) and you may also choose to additionally search
-in non-current revision item revisions (selecting that will search in all
+in non-current item revisions (selecting that will search in all
 revisions).
 
 Simple search queries
 =====================
-Just enter one or few simple words into the query input field and hit ``Enter``.
+Just enter one or more words into the query input field and hit ``Enter``.
 
-If you give multiple words, it will only find documents containing ALL those
+If your query consists of multiple words, it will only find documents containing ALL those
 words ("AND" is the default).
 
 You can use AND (default), OR, NOT to refine your search.
@@ -50,7 +50,7 @@
 ===============
 
 If you want to enter word fragments or if you are not sure about spelling or
-word form, use wildcards for the parts you do not know:
+word form, you can use wildcards for the parts you do not know:
 
 +----------------+-----------------------------------+
 | Wildcard       | Matches                           |
@@ -138,9 +138,9 @@
 =====
 moin uses indexed search - keep in mind that this has some special properties:
 
- * as it is using an index, it is rather fast usually
- * because it is only using the index, it can only find what was put there
- * if you use wildcards, it will still use the index, but in a different, slower way
+ * By using an index, the search is rather usually fast 
+ * Because it is only using an index, it can only find what was put there
+ * If you use wildcards, it will still use the index, but in a different, slower way
 
 E.g.:
 
--- a/quickinstall	Tue Nov 08 03:23:16 2011 +0100
+++ b/quickinstall	Sat Jan 28 17:42:01 2012 +0100
@@ -1,22 +1,45 @@
 #!/bin/bash
 # create a virtual environment in directory $DIR/
+#
+# set PYTHON environment variable to change the python version
+# set DIR environment variable to change the virtual env directory
+# set VIRTUALENV environment variable to change the virtualenv command
+# for example: PYTHON=/usr/bin/pypy DIR=env-pypy ./quickinstall
+#
 # needs: virtualenv, pip
 
-DIR=env
-PYTHON=python
 DLC=dlc
 
-virtualenv --no-site-packages --python $PYTHON $DIR
+# if DIR is not given, use ./env
+if [ -z "$DIR" ]; then
+    DIR=env
+fi
 
-source $DIR/bin/activate
+# find the right python version
+if [ -z "$PYTHON" ]; then
+    for PYTHON in python{2.7,2.6,2,}; do
+        hash $PYTHON 2>&- && break
+    done
+fi
+
+# find the right virtualenv version
+if [ -z "$VIRTUALENV" ]; then
+    for VIRTUALENV in virtualenv{2.7,2.6,2,}; do
+        hash $VIRTUALENV 2>&- && break
+    done
+fi
+
+$VIRTUALENV --no-site-packages --python $PYTHON $DIR || exit 1
+
+source $DIR/bin/activate || exit 1
 
 # first install babel, moin's setup.py will emit a warning if it is not there
-pip install --download-cache=$DLC babel
+pip install --download-cache=$DLC babel || exit 1
 
 # "install" moin2 from repo to the env, this will also install required python
 # packages from pypi. we do this LAST, so that breakage is better visible.
-pip install --download-cache=$DLC -e .
+pip install --download-cache=$DLC -e . || exit 1
 
 # compile the translations
-python setup.py compile_catalog --statistics
+python setup.py compile_catalog --statistics || exit 1
 
--- a/setup.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/setup.py	Sat Jan 28 17:42:01 2012 +0100
@@ -75,6 +75,8 @@
         #'https://github.com/mitsuhiko/werkzeug/tarball/master#egg=Werkzeug-0.7dev',
         # no whoosh 2.3.1 on pypi yet:
         'https://bitbucket.org/mchaput/whoosh/get/19c2df0a94ef.tar.gz#egg=Whoosh-2.3.1',
+        # fixed flask-themes, 0.1.3 does not work for flask 0.8.x, thus we use a faked 0.1.3.1:
+        'https://bitbucket.org/thomaswaldmann/flask-themes/get/24dcc703953f.tar.gz#egg=Flask-Themes-0.1.3.1',
     ],
     install_requires=[
         'blinker>=1.1', # event signalling (e.g. for change notification trigger)
@@ -83,7 +85,7 @@
         'Flask-Babel>=0.7', # i18n support
         'Flask-Cache>=0.3.4', # caching support
         'Flask-Script>=0.3.1', # scripting support
-        'Flask-Themes>=0.1.3', # theme support
+        'Flask-Themes>=0.1.3.1', # theme support
         'emeraldtree>=0.9.0', # xml processing
         'flatland==dev', # repo checkout at revision 269:6c5d262d7eff works
         'Jinja2>=2.6', # template engine
--- a/wikiconfig.py	Tue Nov 08 03:23:16 2011 +0100
+++ b/wikiconfig.py	Sat Jan 28 17:42:01 2012 +0100
@@ -48,8 +48,8 @@
     # Load the interwiki map from intermap.txt:
     interwiki_map = InterWikiMap.from_file(os.path.join(wikiconfig_dir, 'contrib', 'interwiki', 'intermap.txt')).iwmap
     # we must add entries for 'Self' and our interwikiname:
-    interwiki_map[interwikiname] = 'http://localhost:8080/'
-    interwiki_map['Self'] = 'http://localhost:8080/'
+    interwiki_map[interwikiname] = 'http://127.0.0.1:8080/'
+    interwiki_map['Self'] = 'http://127.0.0.1:8080/'
 
     # for now we load some 3rd party stuff from the place within moin where it is currently located,
     # but soon we'll get rid of this stuff:
@@ -76,7 +76,6 @@
 MOINCFG = Config # Flask only likes uppercase stuff
 # Flask settings - see the flask documentation about their meaning
 SECRET_KEY = 'you need to change this so it is really secret'
-SERVER_NAME = "localhost:8080"
 #DEBUG = False # use True for development only, not for public sites!
 #TESTING = False
 #SESSION_COOKIE_NAME = 'session'