changeset 1970:7504b3dc43ac gae

merged default branch into gae branch
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 10 Feb 2013 19:55:14 +0100
parents 5281fec759b1 (current diff) 1bec42a6047b (diff)
children a53c04dd16b6
files .hgignore MoinMoin/app.py MoinMoin/apps/frontend/views.py MoinMoin/constants/keys.py MoinMoin/items/content.py MoinMoin/storage/middleware/indexing.py MoinMoin/templates/layout.html MoinMoin/themes/__init__.py wikiconfig.py
diffstat 57 files changed, 1693 insertions(+), 1311 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sat Feb 09 19:38:31 2013 +0100
+++ b/.hgignore	Sun Feb 10 19:55:14 2013 +0100
@@ -15,6 +15,7 @@
 ^instance/
 ^wikiconfig_.+\.py
 ^MoinMoin/translations/.*/LC_MESSAGES/messages.mo$
+^MoinMoin/_tests/wiki/index/
 ^docs/_build/
 .coverage
 ^.project
--- a/MoinMoin/_tests/__init__.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/_tests/__init__.py	Sun Feb 10 19:55:14 2013 +0100
@@ -33,8 +33,8 @@
         Thus, for testing purposes (e.g. if you need delete rights), it is
         easier to use become_trusted().
     """
-    flaskg.user.profile[NAME] = username
-    flaskg.user.may.name = username
+    flaskg.user.profile[NAME] = [username, ]
+    flaskg.user.may.names = [username, ]  # see security.DefaultSecurityPolicy class
     flaskg.user.valid = 1
 
 
@@ -54,7 +54,7 @@
 
     meta = meta.copy()
     if NAME not in meta:
-        meta[NAME] = name
+        meta[NAME] = [name, ]
     if CONTENTTYPE not in meta:
         meta[CONTENTTYPE] = u'application/octet-stream'
     rev = item.store_revision(meta, StringIO(data), return_rev=True)
--- a/MoinMoin/_tests/test_test_environ.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/_tests/test_test_environ.py	Sun Feb 10 19:55:14 2013 +0100
@@ -33,7 +33,7 @@
         itemname = u"this item shouldn't exist yet"
         assert not storage.has_item(itemname)
         item = storage[itemname]
-        new_rev = item.store_revision({NAME: itemname, CONTENTTYPE: u'text/plain'}, StringIO(''))
+        new_rev = item.store_revision({NAME: [itemname, ], CONTENTTYPE: u'text/plain'}, StringIO(''))
         assert storage.has_item(itemname)
 
 
--- a/MoinMoin/_tests/test_user.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/_tests/test_user.py	Sun Feb 10 19:55:14 2013 +0100
@@ -25,7 +25,7 @@
         email = u"foo@example.org"
         # nonexisting user
         u = user.User(name=name, password=password)
-        assert u.name == name
+        assert u.name == [name, ]
         assert not u.valid
         assert not u.exists()
         # create a user
@@ -33,7 +33,7 @@
         assert ret is None, "create_user returned: {0}".format(ret)
         # existing user
         u = user.User(name=name, password=password)
-        assert u.name == name
+        assert u.name == [name, ]
         assert u.email == email
         assert u.valid
         assert u.exists()
--- a/MoinMoin/app.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/app.py	Sun Feb 10 19:55:14 2013 +0100
@@ -179,7 +179,7 @@
     # A ns_mapping consists of several lines, where each line is made up like this:
     # mountpoint, unprotected backend
     # Just initialize with unprotected backends.
-    app.router = routing.Backend(app.cfg.namespace_mapping)
+    app.router = routing.Backend(app.cfg.namespace_mapping, app.cfg.backend_mapping)
     if app.cfg.create_storage:
         app.router.create()
     app.router.open()
@@ -226,7 +226,7 @@
 
     # if we still have no user obj, create a dummy:
     if not userobj:
-        userobj = user.User(auth_method='invalid')
+        userobj = user.User(name=u'anonymous', auth_method='invalid')
     # if we have a valid user we store it in the session
     if userobj.valid:
         session['user.itemid'] = userobj.itemid
--- a/MoinMoin/apps/admin/templates/admin/userbrowser.html	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/apps/admin/templates/admin/userbrowser.html	Sun Feb 10 19:55:14 2013 +0100
@@ -10,7 +10,7 @@
     </tr>
     {% for u in user_accounts %}
     <tr>
-        <td><a href="{{ url_for('frontend.show_item', item_name=u.name) }}">{{ u.name }}</a>{{ u.disabled and " (%s)" % _("disabled") or ""}}</td>
+        <td><a href="{{ url_for('frontend.show_item', item_name=u.name[0]) }}">{{ u.name|join(',') }}</a>{{ u.disabled and " (%s)" % _("disabled") or ""}}</td>
         <td>{{ u.groups|join(',') }}</td>
         <td>
             {% if u.email %}
--- a/MoinMoin/apps/admin/views.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/apps/admin/views.py	Sun Feb 10 19:55:14 2013 +0100
@@ -231,7 +231,7 @@
     headings = [_('Size'),
                 _('Item name'),
                ]
-    rows = [(rev.meta[SIZE], rev.meta[NAME])
+    rows = [(rev.meta[SIZE], rev.name)
             for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)]
     rows = sorted(rows, reverse=True)
     return render_template('user/itemsize.html',
--- a/MoinMoin/apps/feed/views.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/apps/feed/views.py	Sun Feb 10 19:55:14 2013 +0100
@@ -65,7 +65,7 @@
             query = And([query, Term(NAME_EXACT, item_name), ])
         history = flaskg.storage.search(query, idx_name=ALL_REVS, sortedby=[MTIME], reverse=True, limit=100)
         for rev in history:
-            name = rev.meta[NAME]
+            name = rev.name
             item = rev.item
             this_revid = rev.meta[REVID]
             previous_revid = rev.meta.get(PARENTID)
--- a/MoinMoin/apps/frontend/_tests/test_frontend.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/apps/frontend/_tests/test_frontend.py	Sun Feb 10 19:55:14 2013 +0100
@@ -26,14 +26,17 @@
             viewopts = {}
         if params is None:
             params = {}
-        print 'GET %s' % url_for(viewname, **viewopts)
+
         with self.app.test_client() as c:
-            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
+            for method in ['HEAD', 'GET']:
+                print '%s %s' % (method, url_for(viewname, **viewopts))
+                rv = c.open(url_for(viewname, **viewopts), method=method, data=params)
+                assert rv.status == status
+                assert rv.headers['Content-Type'] in content_types
+                if method == 'GET':
+                    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:
--- a/MoinMoin/apps/frontend/views.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/apps/frontend/views.py	Sun Feb 10 19:55:14 2013 +0100
@@ -31,7 +31,7 @@
 from flask.ext.babel import format_date
 from flask.ext.themes import get_themes_list
 
-from flatland import Form, Enum
+from flatland import Form, Enum, List
 from flatland.validation import Validator
 
 from jinja2 import Markup
@@ -47,7 +47,7 @@
 from MoinMoin.i18n import _, L_, N_
 from MoinMoin.themes import render_template, get_editor_info, contenttype_to_class
 from MoinMoin.apps.frontend import frontend
-from MoinMoin.forms import OptionalText, RequiredText, URL, YourOpenID, YourEmail, RequiredPassword, Checkbox, InlineCheckbox, Select, Tags, Natural, Submit, Hidden, MultiSelect
+from MoinMoin.forms import OptionalText, RequiredText, URL, YourOpenID, YourEmail, RequiredPassword, Checkbox, InlineCheckbox, Select, Names, Tags, Natural, Submit, Hidden, MultiSelect
 from MoinMoin.items import BaseChangeForm, Item, NonExistent
 from MoinMoin.items.content import content_registry
 from MoinMoin import config, user, util
@@ -351,7 +351,7 @@
         rev = item[rev]
     except KeyError:
         abort(404, item_name)
-    content = convert_to_indexable(rev.meta, rev.data)
+    content = convert_to_indexable(rev.meta, rev.data, item_name)
     return Response(content, 200, mimetype='text/plain')
 
 
@@ -490,7 +490,7 @@
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = RevertItemForm.from_defaults()
         TextCha(form).amend_form()
     elif request.method == 'POST':
@@ -516,7 +516,7 @@
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = RenameItemForm.from_defaults()
         TextCha(form).amend_form()
         form['target'] = item.name
@@ -544,7 +544,7 @@
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = DeleteItemForm.from_defaults()
         TextCha(form).amend_form()
     elif request.method == 'POST':
@@ -643,7 +643,7 @@
         abort(403)
     if isinstance(item, NonExistent):
         abort(404, item_name)
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = DestroyItemForm.from_defaults()
         TextCha(form).amend_form()
     elif request.method == 'POST':
@@ -724,10 +724,7 @@
     selected_groups = form['contenttype'].value
     startswith = request.values.get("startswith")
 
-    initials = item.name_initial(item.get_subitem_revs())
-    initials = [initial.upper() for initial in initials]
-    initials = list(set(initials))
-    initials = sorted(initials)
+    initials = item.name_initial(item.get_subitem_revs(), uppercase=True)
 
     dirs, files = item.get_index(startswith, selected_groups)
     # index = sorted(index, key=lambda e: e.relname.lower())
@@ -770,7 +767,7 @@
     q = And([Term(WIKINAME, app.cfg.interwikiname),
              Term(USERID, userid)])
     revs = flaskg.storage.search(q, idx_name=ALL_REVS)
-    return [rev.meta[NAME] for rev in revs]
+    return [rev.name for rev in revs]
 
 
 @frontend.route('/+backrefs/<itemname:item_name>')
@@ -801,7 +798,7 @@
     q = And([Term(WIKINAME, app.cfg.interwikiname),
              Or([Term(ITEMTRANSCLUSIONS, item_name), Term(ITEMLINKS, item_name)])])
     revs = flaskg.storage.search(q)
-    return [rev.meta[NAME] for rev in revs]
+    return [rev.name for rev in revs]
 
 
 @frontend.route('/+history/<itemname:item_name>')
@@ -874,7 +871,7 @@
     existing = set()
     revs = flaskg.storage.documents(wikiname=app.cfg.interwikiname)
     for rev in revs:
-        existing.add(rev.meta[NAME])
+        existing.add(rev.name)
         linked.update(rev.meta.get(ITEMLINKS, []))
         transcluded.update(rev.meta.get(ITEMTRANSCLUSIONS, []))
     return existing, linked, transcluded
@@ -1034,7 +1031,7 @@
         template = 'register.html'
         FormClass = RegistrationForm
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = FormClass.from_defaults()
         if isOpenID:
             oid = request.values.get('openid_openid')
@@ -1124,7 +1121,7 @@
     if not _using_moin_auth():
         return Response('No MoinAuth in auth list', 403)
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = PasswordLostForm.from_defaults()
     elif request.method == 'POST':
         form = PasswordLostForm.from_flat(request.form)
@@ -1187,7 +1184,7 @@
     if not _using_moin_auth():
         return Response('No MoinAuth in auth list', 403)
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = PasswordRecoveryForm.from_defaults()
         form.update(request.values)
     elif request.method == 'POST':
@@ -1254,7 +1251,7 @@
     if flaskg._login_multistage_name == 'openid':
         return Response(flaskg._login_multistage, mimetype='text/html')
 
-    if request.method == 'GET':
+    if request.method in ['GET', 'HEAD']:
         form = LoginForm.from_defaults()
         for authmethod in app.cfg.auth:
             hint = authmethod.login_hint()
@@ -1346,8 +1343,8 @@
     # these forms can't be global because we need app object, which is only available within a request:
     class UserSettingsPersonalForm(Form):
         name = 'usersettings_personal' # "name" is duplicate
-        name = RequiredText.using(label=L_('Name')).with_properties(placeholder=L_("The login name you want to use"))
-        aliasname = OptionalText.using(label=L_('Alias-Name')).with_properties(placeholder=L_("Your alias name (informational)"))
+        name = Names.using(label=L_('Names')).with_properties(placeholder=L_("The login names you want to use"))
+        display_name = OptionalText.using(label=L_('Display-Name')).with_properties(placeholder=L_("Your display name (informational)"))
         openid = YourOpenID.using(optional=True)
         #timezones_keys = sorted(Locale('en').time_zones.keys())
         timezones_keys = [unicode(tz) for tz in pytz.common_timezones]
@@ -1417,10 +1414,13 @@
                             # 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 set(form['name'].value) != set(flaskg.user.name):
+                            new_names = set(form['name'].value) - set(flaskg.user.name)
+                            for name in new_names:
+                                if user.search_users(name_exact=name):
+                                    # duplicate name
+                                    response['flash'].append((_("The username %(name)r is already in use.", name=name), "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):
@@ -1645,7 +1645,8 @@
     :rtype: tuple
     :returns: start word, end word, matches dict
     """
-    item_names = [rev.meta[NAME] for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)]
+    item_names = [rev.name for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)
+                  if rev.name is not None]
     if item_name in item_names:
         item_names.remove(item_name)
     # Get matches using wiki way, start and end of word
@@ -1820,7 +1821,7 @@
     tags_counts = {}
     for rev in revs:
         tags = rev.meta.get(TAGS, [])
-        logging.debug("name {0!r} rev {1} tags {2!r}".format(rev.meta[NAME], rev.meta[REVID], tags))
+        logging.debug("name {0!r} rev {1} tags {2!r}".format(rev.name, rev.meta[REVID], tags))
         for tag in tags:
             tags_counts[tag] = tags_counts.setdefault(tag, 0) + 1
     tags_counts = sorted(tags_counts.items())
@@ -1854,7 +1855,7 @@
     """
     query = And([Term(WIKINAME, app.cfg.interwikiname), Term(TAGS, tag), ])
     revs = flaskg.storage.search(query, sortedby=NAME_EXACT, limit=None)
-    item_names = [rev.meta[NAME] for rev in revs]
+    item_names = [rev.name for rev in revs]
     return render_template("link_list_no_item_panel.html",
                            headline=_("Items tagged with %(tag)s", tag=tag),
                            item_name=tag,
--- a/MoinMoin/apps/misc/views.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/apps/misc/views.py	Sun Feb 10 19:55:14 2013 +0100
@@ -32,7 +32,7 @@
 
     sitemap = []
     for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname):
-        name = rev.meta[NAME]
+        name = rev.name
         mtime = rev.meta[MTIME]
         if False: # was: wikiutil.isSystemItem(name)   XXX add back later, when we have that in the index
             if not SITEMAP_HAS_SYSTEM_ITEMS:
@@ -66,6 +66,6 @@
     See: http://usemod.com/cgi-bin/mb.pl?SisterSitesImplementationGuide
     """
     # XXX we currently also get deleted items, fix this
-    item_names = sorted([rev.meta[NAME] for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)])
+    item_names = sorted([rev.name for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)])
     content = render_template('misc/urls_names.txt', item_names=item_names)
     return Response(content, mimetype='text/plain')
--- a/MoinMoin/auth/_tests/test_auth.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/auth/_tests/test_auth.py	Sun Feb 10 19:55:14 2013 +0100
@@ -20,7 +20,7 @@
         auth = [GivenAuth(user_name=u'JoeDoe', autocreate=True), ]
 
     def test(self):
-        assert flaskg.user.name == u'JoeDoe'
+        assert flaskg.user.name == [u'JoeDoe', ]
 
 
 class TestGivenAuth(object):
@@ -52,7 +52,7 @@
         create_user(u'Test_User', u'test_pass', u'test@moinmoin.org')
         test_user, bool_value = givenauth_obj.request(flaskg.user)
         assert test_user.valid
-        assert test_user.name == u'Test_User'
+        assert test_user.name == [u'Test_User', ]
 
 def test_handle_login():
     # no messages in the beginning
@@ -60,7 +60,7 @@
     test_user1 = handle_login(flaskg.user, login_username='test_user', login_password='test_password', stage='moin')
     test_login_message = [u'Invalid username or password.']
     assert flaskg._login_messages == test_login_message
-    assert test_user1.name == u'anonymous'
+    assert test_user1.name0 == u'anonymous'
     assert not test_user1.valid
     # pop the message
     flaskg._login_messages.pop()
@@ -72,7 +72,7 @@
     test_user, bool_value = givenauth_obj.request(flaskg.user)
     test_user2 = handle_login(test_user, login_username='Test_User', login_password='test_pass', stage='moin')
     assert not flaskg._login_messages
-    assert test_user2.name == u'Test_User'
+    assert test_user2.name == [u'Test_User', ]
     assert test_user2.valid
 
 def test_get_multistage_continuation_url():
--- a/MoinMoin/auth/_tests/test_http.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/auth/_tests/test_http.py	Sun Feb 10 19:55:14 2013 +0100
@@ -34,11 +34,11 @@
         httpauthmoin_obj = HTTPAuthMoin()
         test_user, bool_val = httpauthmoin_obj.request(flaskg.user)
         assert test_user.valid
-        assert test_user.name == u'ValidUser'
+        assert test_user.name == [u'ValidUser', ]
         assert bool_val
 
         # when auth_method is not 'http'
         flaskg.user.auth_method = 'invalid'
         test_user, bool_val = httpauthmoin_obj.request(flaskg.user)
         assert not test_user.valid
-        assert test_user.name == u'anonymous'
+        assert test_user.name0 == u'anonymous'
--- a/MoinMoin/auth/_tests/test_log.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/auth/_tests/test_log.py	Sun Feb 10 19:55:14 2013 +0100
@@ -19,13 +19,13 @@
         result = authlog_obj.login(flaskg.user)
         assert result.continue_flag
         test_user_obj = result.user_obj
-        assert test_user_obj.name == u'anonymous'
+        assert test_user_obj.name0 == u'anonymous'
 
     def test_request(self):
         authlog_obj = AuthLog()
         result = authlog_obj.request(flaskg.user)
         test_user, bool_value = result
-        assert test_user.name == u'anonymous'
+        assert test_user.name0 == u'anonymous'
         assert not test_user.valid
         assert bool_value
 
@@ -33,6 +33,6 @@
         authlog_obj = AuthLog()
         result = authlog_obj.logout(flaskg.user)
         test_user, bool_value = result
-        assert test_user.name == u'anonymous'
+        assert test_user.name0 == u'anonymous'
         assert not test_user.valid
         assert bool_value
--- a/MoinMoin/auth/ldap_login.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/auth/ldap_login.py	Sun Feb 10 19:55:14 2013 +0100
@@ -14,7 +14,7 @@
     "Can't contact LDAP server" - more recent debian installations have tls
     support in libldap2 (see dependency on gnutls) and also in python-ldap.
 
-    TODO: allow more configuration (alias name, ...) by using callables as parameters
+    TODO: allow more configuration (display name, ...) by using callables as parameters
 """
 
 from MoinMoin import log
@@ -70,7 +70,7 @@
         # some attribute names we use to extract information from LDAP:
         givenname_attribute=None, # ('givenName') ldap attribute we get the first name from
         surname_attribute=None, # ('sn') ldap attribute we get the family name from
-        aliasname_attribute=None, # ('displayName') ldap attribute we get the aliasname from
+        displayname_attribute=None, # ('displayName') ldap attribute we get the display_name from
         email_attribute=None, # ('mail') ldap attribute we get the email address from
         email_callback=None, # called to make up email address
         name_callback=None, # called to use a Wiki name different from the login name
@@ -99,7 +99,7 @@
 
         self.givenname_attribute = givenname_attribute
         self.surname_attribute = surname_attribute
-        self.aliasname_attribute = aliasname_attribute
+        self.displayname_attribute = displayname_attribute
         self.email_attribute = email_attribute
         self.email_callback = email_callback
         self.name_callback = name_callback
@@ -177,7 +177,7 @@
                 logging.debug("Searching {0!r}".format(filterstr))
                 attrs = [getattr(self, attr) for attr in [
                                          'email_attribute',
-                                         'aliasname_attribute',
+                                         'displayname_attribute',
                                          'surname_attribute',
                                          'givenname_attribute',
                                          ] if getattr(self, attr) is not None]
@@ -215,19 +215,19 @@
                 else:
                     email = self.email_callback(ldap_dict)
 
-                aliasname = ''
+                display_name = ''
                 try:
-                    aliasname = ldap_dict[self.aliasname_attribute][0]
+                    display_name = ldap_dict[self.displayname_attribute][0]
                 except (KeyError, IndexError):
                     pass
-                if not aliasname:
+                if not display_name:
                     sn = ldap_dict.get(self.surname_attribute, [''])[0]
                     gn = ldap_dict.get(self.givenname_attribute, [''])[0]
                     if sn and gn:
-                        aliasname = "{0}, {1}".format(sn, gn)
+                        display_name = "{0}, {1}".format(sn, gn)
                     elif sn:
-                        aliasname = sn
-                aliasname = aliasname.decode(coding)
+                        display_name = sn
+                display_name = display_name.decode(coding)
 
                 if self.name_callback:
                     username = self.name_callback(ldap_dict)
@@ -240,8 +240,8 @@
                     u = user.User(auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author', ),
                                   trusted=self.trusted)
                 u.name = username
-                u.aliasname = aliasname
-                logging.debug("creating user object with name {0!r} email {1!r} alias {2!r}".format(username, email, aliasname))
+                u.display_name = display_name
+                logging.debug("creating user object with name {0!r} email {1!r} display name {2!r}".format(username, email, display_name))
 
             except ldap.INVALID_CREDENTIALS as err:
                 logging.debug("invalid credentials (wrong password?) for dn {0!r} (username: {1!r})".format(dn, username))
--- a/MoinMoin/config/default.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/config/default.py	Sun Feb 10 19:55:14 2013 +0100
@@ -116,6 +116,10 @@
             raise error.ConfigurationError("No storage configuration specified! You need to define a namespace_mapping. "
                                            "For further reference, please see HelpOnStorageConfiguration.")
 
+        if self.backend_mapping is None:
+            raise error.ConfigurationError("No storage configuration specified! You need to define a backend_mapping. " +
+                                           "For further reference, please see HelpOnStorageConfiguration.")
+
         if self.acl_mapping is None:
             raise error.ConfigurationError("No acl configuration specified! You need to define a acl_mapping. "
                                            "For further reference, please see HelpOnStorageConfiguration.")
@@ -433,8 +437,11 @@
     ('interwiki_map', {},
      "Dictionary of wiki_name -> wiki_url"),
     ('namespace_mapping', None,
-    "This needs to point to a list of tuples, each tuple containing: Namespace identifier, backend. " +
-    "E.g.: [('/', FSBackend('wiki/data')), ]. Please see HelpOnStorageConfiguration for further reference."),
+    "A list of tuples, each tuple containing: Namespace identifier, backend name. " +
+    "E.g.: [('', 'default')), ]. Please see HelpOnStorageConfiguration for further reference."),
+    ('backend_mapping', None,
+    "A dictionary that maps backend names to backends. " +
+    "E.g.: {'default': Backend(), }. Please see HelpOnStorageConfiguration for further reference."),
     ('acl_mapping', None,
     "This needs to point to a list of tuples, each tuple containing: name prefix, acl protection to be applied to matching items. " +
     "E.g.: [('', dict(default='All:read,write,create')), ]. Please see HelpOnStorageConfiguration for further reference."),
@@ -460,8 +467,8 @@
   'user': ('User Preferences', None, (
     ('user_defaults',
       dict(
-        name=u'anonymous',
-        aliasname=None,
+        name=[],
+        display_name=None,
         email=None,
         openid=None,
         css_url=None,
--- a/MoinMoin/conftest.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/conftest.py	Sun Feb 10 19:55:14 2013 +0100
@@ -44,9 +44,11 @@
 
 
 def init_test_app(given_config):
-    namespace_mapping, acl_mapping = create_simple_mapping("stores:memory:", given_config.content_acl)
+    namespace_mapping, backend_mapping, acl_mapping = \
+        create_simple_mapping("stores:memory:", given_config.content_acl)
     more_config = dict(
         namespace_mapping=namespace_mapping,
+        backend_mapping=backend_mapping,
         acl_mapping=acl_mapping,
         create_storage=True, # create a fresh storage at each app start
         destroy_storage=True, # kill all storage contents at app shutdown
--- a/MoinMoin/constants/keys.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/constants/keys.py	Sun Feb 10 19:55:14 2013 +0100
@@ -12,6 +12,7 @@
 # metadata keys
 NAME = "name"
 NAME_OLD = "name_old"
+NAMESPACE = "namespace"
 
 # if an item is reverted, we store the revision number we used for reverting there:
 REVERTED_TO = "reverted_to"
@@ -69,7 +70,7 @@
 # stuff from user profiles / for whoosh index
 EMAIL = "email"
 OPENID = "openid"
-ALIASNAME = "aliasname"
+DISPLAY_NAME = "display_name"
 THEME_NAME = "theme_name"
 LOCALE = "locale"
 TIMEZONE = "timezone"
@@ -90,9 +91,12 @@
 DISABLED = "disabled"
 GAE_USER_ID = "gae_user_id"
 
+# in which backend is some revision stored?
+BACKENDNAME = "backendname"
+
 USEROBJ_ATTRS = [
     # User objects proxy these attributes of the UserProfile objects:
-    NAME, DISABLED, ITEMID, ALIASNAME, ENC_PASSWORD, EMAIL, OPENID,
+    NAME, DISABLED, ITEMID, DISPLAY_NAME, ENC_PASSWORD, EMAIL, OPENID,
     MAILTO_AUTHOR, SHOW_COMMENTS, RESULTS_PER_PAGE, EDIT_ON_DOUBLECLICK, SCROLL_PAGE_AFTER_EDIT,
     EDIT_ROWS, THEME_NAME, LOCALE, TIMEZONE, SUBSCRIBED_ITEMS, QUICKLINKS,
     CSS_URL,
--- a/MoinMoin/converter/macro.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/converter/macro.py	Sun Feb 10 19:55:14 2013 +0100
@@ -42,7 +42,7 @@
 
         type = Type(type)
         if not (type.type == 'x-moin' and type.subtype == 'macro'):
-            logging.debug("not a macro, skipping: %r" % type)
+            logging.debug("not a macro, skipping: %r" % (type, ))
             return
 
         name = type.parameters['name']
--- a/MoinMoin/datastruct/backends/_tests/test_wiki_groups.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/datastruct/backends/_tests/test_wiki_groups.py	Sun Feb 10 19:55:14 2013 +0100
@@ -44,7 +44,7 @@
         assert u'ExampleUser' in flaskg.groups[u'SomeGroup']
         pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups[u'AnotherGroup'])
 
-        item = update_item(u'SomeGroup', {NAME: u'AnotherGroup', USERGROUP: ["ExampleUser"]}, DATA)
+        item = update_item(u'SomeGroup', {NAME: [u'AnotherGroup', ], USERGROUP: ["ExampleUser"]}, DATA)
         assert u'ExampleUser' in flaskg.groups[u'AnotherGroup']
         pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups[u'SomeGroup'])
 
--- a/MoinMoin/forms.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/forms.py	Sun Feb 10 19:55:14 2013 +0100
@@ -90,6 +90,8 @@
 
 Tags = MyJoinedString.of(String).with_properties(widget=WIDGET_TEXT).using(label=L_('Tags'), optional=True, separator=', ', separator_regex=re.compile(r'\s*,\s*'))
 
+Names = MyJoinedString.of(String).with_properties(widget=WIDGET_TEXT).using(label=L_('Names'), optional=True, separator=', ', separator_regex=re.compile(r'\s*,\s*'))
+
 Search = Text.using(default=u'', optional=True).with_properties(widget=WIDGET_SEARCH, placeholder=L_("Search Query"))
 
 _Integer = Integer.validated_by(Converted())
--- a/MoinMoin/items/__init__.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/items/__init__.py	Sun Feb 10 19:55:14 2013 +0100
@@ -17,8 +17,6 @@
     Each class in this module corresponds to an itemtype.
 """
 
-import time
-import itertools
 import json
 from StringIO import StringIO
 from collections import namedtuple
@@ -40,7 +38,7 @@
 from MoinMoin.security.textcha import TextCha, TextChaizedForm
 from MoinMoin.signalling import item_modified
 from MoinMoin.storage.middleware.protecting import AccessDenied
-from MoinMoin.storage.error import NoSuchItemError, NoSuchRevisionError, StorageError
+from MoinMoin.storage.error import NoSuchItemError, NoSuchRevisionError
 from MoinMoin.i18n import L_
 from MoinMoin.themes import render_template
 from MoinMoin.util.mime import Type
@@ -110,7 +108,7 @@
         self.data = StringIO('')
         self.revid = None
         if self.item:
-            self.meta[NAME] = self.item.name
+            self.meta[NAME] = [self.item.name]
 
 
 class DummyItem(object):
@@ -291,7 +289,6 @@
             SYSITEM_VERSION,
             NAME_OLD,
             # are automatically implanted when saving
-            NAME,
             ITEMID, REVID, DATAID,
             HASH_ALGORITHM,
             SIZE,
@@ -327,31 +324,33 @@
             meta[PARENTID] = revid
         return meta
 
-    def _rename(self, name, comment, action):
-        self._save(self.meta, self.content.data, name=name, action=action, comment=comment)
-        old_prefixlen = len(self.subitems_prefix)
-        new_prefix = name + '/'
+    def _rename(self, name, comment, action, delete=False):
+        self._save(self.meta, self.content.data, name=name, action=action, comment=comment, delete=delete)
+        old_prefix = self.subitems_prefix
+        old_prefixlen = len(old_prefix)
+        if not delete:
+            new_prefix = name + '/'
         for child in self.get_subitem_revs():
-            child_oldname = child.meta[NAME]
-            child_newname = new_prefix + child_oldname[old_prefixlen:]
-            item = Item.create(child_oldname)
-            item._save(item.meta, item.content.data, name=child_newname, action=action, comment=comment)
+            for child_oldname in child.meta[NAME]:
+                if child_oldname.startswith(old_prefix):
+                    if delete:
+                        child_newname = None
+                    else:  # rename
+                        child_newname = new_prefix + child_oldname[old_prefixlen:]
+                    item = Item.create(child_oldname)
+                    item._save(item.meta, item.content.data, name=child_newname, action=action, comment=comment, delete=delete)
 
     def rename(self, name, comment=u''):
         """
-        rename this item to item <name>
+        rename this item to item <name> (replace current name by another name in the NAME list)
         """
         return self._rename(name, comment, action=u'RENAME')
 
     def delete(self, comment=u''):
         """
-        delete this item
+        delete this item (remove current name from NAME list)
         """
-        trash_prefix = u'Trash/' # XXX move to config
-        now = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
-        # make trash name unique by including timestamp:
-        trashname = u'{0}{1} ({2} UTC)'.format(trash_prefix, self.name, now)
-        return self._rename(trashname, comment, action=u'TRASH')
+        return self._rename(None, comment, action=u'TRASH', delete=True)
 
     def revert(self, comment=u''):
         return self._save(self.meta, self.content.data, action=u'REVERT', comment=comment)
@@ -406,7 +405,8 @@
         """
         raise NotImplementedError
 
-    def _save(self, meta, data=None, name=None, action=u'SAVE', contenttype_guessed=None, comment=u'', overwrite=False):
+    def _save(self, meta, data=None, name=None, action=u'SAVE', contenttype_guessed=None, comment=u'',
+              overwrite=False, delete=False):
         backend = flaskg.storage
         storage_item = backend[self.name]
         try:
@@ -425,9 +425,20 @@
         if name is None:
             name = self.name
         oldname = meta.get(NAME)
-        if oldname and oldname != name:
-            meta[NAME_OLD] = oldname
-        meta[NAME] = name
+        if oldname:
+            if not isinstance(oldname, list):
+                oldname = [oldname]
+            if delete or name not in oldname: # this is a delete or rename
+                meta[NAME_OLD] = oldname[:]
+                try:
+                    oldname.remove(self.name)
+                except ValueError:
+                    pass
+                if not delete:
+                    oldname.append(name)
+                meta[NAME] = oldname
+        else:
+            meta[NAME] = [name]
 
         if comment:
             meta[COMMENT] = unicode(comment)
@@ -504,21 +515,23 @@
         added_dir_relnames = set()
 
         for rev in subitems:
-            fullname = rev.meta[NAME]
-            relname = fullname[prefixlen:]
-            if '/' in relname:
-                # Find the *direct* subitem that is the ancestor of current
-                # (indirect) subitem. e.g. suppose when the index root is
-                # 'foo', and current item (`rev`) is 'foo/bar/lorem/ipsum',
-                # 'foo/bar' will be found.
-                direct_relname = relname.partition('/')[0]
-                if direct_relname not in added_dir_relnames:
-                    added_dir_relnames.add(direct_relname)
-                    direct_fullname = prefix + direct_relname
-                    direct_rev = get_storage_revision(direct_fullname)
-                    dirs.append(IndexEntry(direct_relname, direct_rev.meta))
-            else:
-                files.append(IndexEntry(relname, rev.meta))
+            fullnames = rev.meta[NAME]
+            for fullname in fullnames:
+                if fullname.startswith(prefix):
+                    relname = fullname[prefixlen:]
+                    if '/' in relname:
+                        # Find the *direct* subitem that is the ancestor of current
+                        # (indirect) subitem. e.g. suppose when the index root is
+                        # 'foo', and current item (`rev`) is 'foo/bar/lorem/ipsum',
+                        # 'foo/bar' will be found.
+                        direct_relname = relname.partition('/')[0]
+                        if direct_relname not in added_dir_relnames:
+                            added_dir_relnames.add(direct_relname)
+                            direct_fullname = prefix + direct_relname
+                            direct_rev = get_storage_revision(direct_fullname)
+                            dirs.append(IndexEntry(direct_relname, direct_rev.meta))
+                    else:
+                        files.append(IndexEntry(relname, rev.meta))
 
         return dirs, files
 
@@ -555,10 +568,24 @@
 
     index_template = 'index.html'
 
-    def name_initial(self, subitems):
-        prefixlen = len(self.subitems_prefix)
-        initials = [(item.meta[NAME][prefixlen]) for item in subitems]
-        return initials
+    def name_initial(self, subitems, uppercase=False, lowercase=False):
+        """
+        return a sorted list of first characters of subitem names,
+        optionally all uppercased or lowercased.
+        """
+        prefix = self.subitems_prefix
+        prefixlen = len(prefix)
+        initials = set()
+        for item in subitems:
+            for name in item.meta[NAME]:
+                if name.startswith(prefix):
+                    initial = name[prefixlen]
+                    if uppercase:
+                        initial = initial.upper()
+                    elif lowercase:
+                        initial = initial.lower()
+                    initials.add(initial)
+        return sorted(list(initials))
 
     delete_template = 'delete.html'
     destroy_template = 'destroy.html'
@@ -622,7 +649,7 @@
 
     def do_modify(self):
         method = request.method
-        if method == 'GET':
+        if method in ['GET', 'HEAD']:
             if isinstance(self.content, NonExistentContent):
                 return render_template('modify_select_contenttype.html',
                                        item_name=self.name,
@@ -715,6 +742,22 @@
                                itemtypes=item_registry.shown_entries,
                               )
 
+    def rename(self, name, comment=u''):
+        # pointless for non-existing items
+        pass
+
+    def delete(self, comment=u''):
+        # pointless for non-existing items
+        pass
+
+    def revert(self, comment=u''):
+        # pointless for non-existing items
+        pass
+
+    def destroy(self, comment=u'', destroy_item=False):
+        # pointless for non-existing items
+        pass
+
 
 from ..util.pysupport import load_package_modules
 load_package_modules(__name__, __path__)
--- a/MoinMoin/items/_tests/test_Item.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/items/_tests/test_Item.py	Sun Feb 10 19:55:14 2013 +0100
@@ -37,9 +37,10 @@
     return [(MixedIndexEntry(relname, Item.create('/'.join((basename, relname))).meta, hassubitem))
             for relname, hassubitem in spec]
 
+
 class TestItem(object):
 
-    def testNonExistent(self):
+    def _testNonExistent(self):
         item = Item.create(u'DoesNotExist')
         assert isinstance(item, NonExistent)
         meta, data = item.meta, item.content.data
@@ -130,31 +131,52 @@
     def test_meta_filter(self):
         name = u'Test_item'
         contenttype = u'text/plain;charset=utf-8'
-        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, 'name': 'test_name'}
+        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: [u'test_name'], 'address': u'1.2.3.4'}
         item = Item.create(name)
         result = Item.meta_filter(item, meta)
         # keys like NAME, ITEMID, REVID, DATAID are filtered
-        expected = {'test_key': 'test_val', CONTENTTYPE: contenttype}
+        expected = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: [u'test_name']}
         assert result == expected
 
     def test_meta_dict_to_text(self):
         name = u'Test_item'
         contenttype = u'text/plain;charset=utf-8'
-        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, 'name': 'test_name'}
+        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: [u'test_name']}
         item = Item.create(name)
         result = Item.meta_dict_to_text(item, meta)
-        expected = '{\n  "contenttype": "text/plain;charset=utf-8", \n  "test_key": "test_val"\n}'
+        expected = '{\n  "contenttype": "text/plain;charset=utf-8", \n  "name": [\n    "test_name"\n  ], \n  "test_key": "test_val"\n}'
         assert result == expected
 
     def test_meta_text_to_dict(self):
         name = u'Test_item'
         contenttype = u'text/plain;charset=utf-8'
-        text = '{\n  "contenttype": "text/plain;charset=utf-8", \n  "test_key": "test_val", \n "name": "test_name" \n}'
+        text = '{\n  "contenttype": "text/plain;charset=utf-8", \n  "test_key": "test_val", \n "name": ["test_name"] \n}'
         item = Item.create(name)
         result = Item.meta_text_to_dict(item, text)
-        expected = {'test_key': 'test_val', CONTENTTYPE: contenttype}
+        expected = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: [u"test_name"]}
         assert result == expected
 
+    def test_item_can_have_several_names(self):
+        content = u"This is page content"
+
+        update_item(u'Page',
+                    {NAME: [u'Page',
+                            u'Another name',
+                            ],
+                     CONTENTTYPE: u'text/x.moin.wiki'}, content)
+
+        item1 = Item.create(u'Page')
+        assert item1.name == u'Page'
+        assert item1.meta[CONTENTTYPE] == 'text/x.moin.wiki'
+        assert item1.content.data == content
+
+        item2 = Item.create(u'Another name')
+        assert item2.name == u'Another name'
+        assert item2.meta[CONTENTTYPE] == 'text/x.moin.wiki'
+        assert item2.content.data == content
+
+        assert item1.rev.revid == item2.rev.revid
+
     def test_rename(self):
         name = u'Test_Item'
         contenttype = u'text/plain;charset=utf-8'
@@ -177,10 +199,43 @@
         # item at new name and its contents after renaming
         item = Item.create(new_name)
         assert item.name == u'Test_new_Item'
-        assert item.meta['name_old'] == u'Test_Item'
+        assert item.meta['name_old'] == [u'Test_Item']
         assert item.meta['comment'] == u'renamed'
         assert item.content.data == u'test_data'
 
+    def test_rename_acts_only_in_active_name_in_case_there_are_several_names(self):
+        content = u"This is page content"
+
+        update_item(u'Page',
+                    {NAME: [u'First',
+                            u'Second',
+                            u'Third',
+                            ],
+                     CONTENTTYPE: u'text/x.moin.wiki'}, content)
+
+        item = Item.create(u'Second')
+        item.rename(u'New name', comment=u'renamed')
+
+        item1 = Item.create(u'First')
+        assert item1.name == u'First'
+        assert item1.meta[CONTENTTYPE] == 'text/x.moin.wiki'
+        assert item1.content.data == content
+
+        item2 = Item.create(u'New name')
+        assert item2.name == u'New name'
+        assert item2.meta[CONTENTTYPE] == 'text/x.moin.wiki'
+        assert item2.content.data == content
+
+        item3 = Item.create(u'Third')
+        assert item3.name == u'Third'
+        assert item3.meta[CONTENTTYPE] == 'text/x.moin.wiki'
+        assert item3.content.data == content
+
+        assert item1.rev.revid == item2.rev.revid == item3.rev.revid
+
+        item4 = Item.create(u'Second')
+        assert item4.meta[CONTENTTYPE] == 'application/x-nonexistent'
+
     def test_rename_recursion(self):
         update_item(u'Page', {CONTENTTYPE: u'text/x.moin.wiki'}, u'Page 1')
         update_item(u'Page/Child', {CONTENTTYPE: u'text/x.moin.wiki'}, u'this is child')
@@ -203,22 +258,51 @@
         # item at new name and its contents after renaming
         item = Item.create(u'Renamed_Page')
         assert item.name == u'Renamed_Page'
-        assert item.meta['name_old'] == u'Page'
+        assert item.meta['name_old'] == [u'Page']
         assert item.meta['comment'] == u'renamed'
         assert item.content.data == u'Page 1'
 
         item = Item.create(u'Renamed_Page/Child')
         assert item.name == u'Renamed_Page/Child'
-        assert item.meta['name_old'] == u'Page/Child'
+        assert item.meta['name_old'] == [u'Page/Child']
         assert item.meta['comment'] == u'renamed'
         assert item.content.data == u'this is child'
 
         item = Item.create(u'Renamed_Page/Child/Another')
         assert item.name == u'Renamed_Page/Child/Another'
-        assert item.meta['name_old'] == u'Page/Child/Another'
+        assert item.meta['name_old'] == [u'Page/Child/Another']
         assert item.meta['comment'] == u'renamed'
         assert item.content.data == u'another child'
 
+    def test_rename_recursion_with_multiple_names_and_children(self):
+        update_item(u'Foo',
+                    {CONTENTTYPE: u'text/x.moin.wiki',
+                         NAME: [u'Other', u'Page', u'Foo']},
+                    u'Parent')
+        update_item(u'Page/Child', {CONTENTTYPE: u'text/x.moin.wiki'}, u'Child of Page')
+        update_item(u'Other/Child2', {CONTENTTYPE: u'text/x.moin.wiki'}, u'Child of Other')
+        update_item(u'Another',
+                    {CONTENTTYPE: u'text/x.moin.wiki',
+                     NAME: [u'Another', u'Page/Second']
+                         },
+                    u'Both')
+        update_item(u'Page/Second/Child', {CONTENTTYPE: u'text/x.moin.wiki'}, u'Child of Second')
+        update_item(u'Another/Child', {CONTENTTYPE: u'text/x.moin.wiki'}, u'Child of Another')
+
+        item = Item.create(u'Page')
+
+        item.rename(u'Renamed', comment=u'renamed')
+
+        assert Item.create(u'Page/Child').meta[CONTENTTYPE] == 'application/x-nonexistent'
+        assert Item.create(u'Renamed/Child').content.data == u'Child of Page'
+        assert Item.create(u'Page/Second').meta[CONTENTTYPE] == 'application/x-nonexistent'
+        assert Item.create(u'Renamed/Second').content.data == u'Both'
+        assert Item.create(u'Another').content.data == u'Both'
+        assert Item.create(u'Page/Second/Child').meta[CONTENTTYPE] == 'application/x-nonexistent'
+        assert Item.create(u'Renamed/Second/Child').content.data == u'Child of Second'
+        assert Item.create(u'Other/Child2').content.data == u'Child of Other'
+        assert Item.create(u'Another/Child').content.data == u'Child of Another'
+
     def test_delete(self):
         name = u'Test_Item2'
         contenttype = u'text/plain;charset=utf-8'
--- a/MoinMoin/items/blog.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/items/blog.py	Sun Feb 10 19:55:14 2013 +0100
@@ -20,7 +20,7 @@
 from MoinMoin.themes import render_template
 from MoinMoin.forms import OptionalText, Tags, DateTime
 from MoinMoin.storage.middleware.protecting import AccessDenied
-from MoinMoin.constants.keys import NAME, NAME_EXACT, WIKINAME, ITEMTYPE, MTIME, PTIME, TAGS
+from MoinMoin.constants.keys import NAME_EXACT, WIKINAME, ITEMTYPE, MTIME, PTIME, TAGS
 from MoinMoin.items import Item, Default, register, BaseMetaForm
 
 
@@ -81,7 +81,7 @@
         ptime_sort_facet = FunctionFacet(ptime_sort_key)
 
         revs = flaskg.storage.search(query, sortedby=ptime_sort_facet, reverse=True, limit=None)
-        blog_entry_items = [Item.create(rev.meta[NAME], rev_id=rev.revid) for rev in revs]
+        blog_entry_items = [Item.create(rev.name, rev_id=rev.revid) for rev in revs]
         return render_template('blog/main.html',
                                item_name=self.name,
                                blog_item=self,
--- a/MoinMoin/items/content.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/items/content.py	Sun Feb 10 19:55:14 2013 +0100
@@ -274,7 +274,7 @@
             terms.append(Term(CONTENTTYPE, contenttype))
         query = And(terms)
         revs = flaskg.storage.search(query, sortedby=NAME_EXACT, limit=None)
-        return [rev.meta[NAME] for rev in revs]
+        return [rev.name for rev in revs]
 
 
 @register
--- a/MoinMoin/items/ticket.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/items/ticket.py	Sun Feb 10 19:55:14 2013 +0100
@@ -103,7 +103,7 @@
             return self.do_modify()
 
     def do_modify(self):
-        if request.method == 'GET':
+        if request.method in ['GET', 'HEAD']:
             form = TicketForm.from_item(self)
         elif request.method == 'POST':
             form = TicketForm.from_request(request)
--- a/MoinMoin/script/account/create.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/script/account/create.py	Sun Feb 10 19:55:14 2013 +0100
@@ -20,8 +20,8 @@
     option_list = (
         Option('--name', '-n', required=True, dest='name', type=unicode,
                help="Set the wiki user name to NAME."),
-        Option('--alias', '-a', required=False, dest="aliasname", type=unicode,
-               help="Set the wiki user alias name to ALIAS (e.g. the real name if NAME is cryptic)."),
+        Option('--display_name', '-d', required=False, dest="display_name", type=unicode,
+               help="Set the wiki user's display name to DISPLAY_NAME (e.g. in case the NAME is cryptic)."),
         Option('--email', '-e', required=True, dest='email', type=unicode,
                help="Set the user's email address to EMAIL."),
         Option('--openid', '-o', required=False, dest='openid', type=unicode,
@@ -30,7 +30,7 @@
                help="Set the user's password to PASSWORD."),
     )
 
-    def run(self, name, aliasname, email, openid, password):
+    def run(self, name, display_name, email, openid, password):
         before_wiki()
         msg = user.create_user(username=name,
                                password=password,
--- a/MoinMoin/script/maint/modify_item.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/script/maint/modify_item.py	Sun Feb 10 19:55:14 2013 +0100
@@ -12,7 +12,7 @@
 from flask import g as flaskg
 from flask.ext.script import Command, Option
 
-from MoinMoin.config import NAME, CURRENT, REVID, DATAID, SIZE, HASH_ALGORITHM
+from MoinMoin.config import CURRENT, ITEMID, REVID, DATAID, SIZE, HASH_ALGORITHM
 
 
 class GetItem(Command):
@@ -55,7 +55,6 @@
             meta = mf.read()
         meta = meta.decode('utf-8')
         meta = json.loads(meta)
-        name = meta[NAME]
         to_kill = [SIZE, HASH_ALGORITHM, # gets re-computed automatically
                    DATAID,
                   ]
@@ -64,6 +63,7 @@
         if not overwrite:
             # if we remove the REVID, it will create a new one and store under the new one
             meta.pop(REVID, None)
-        item = app.storage[name]
+        query = {ITEMID: meta[ITEMID]}
+        item = app.storage.get_item(**query)
         with open(data_file, 'rb') as df:
             item.store_revision(meta, df, overwrite=overwrite)
--- a/MoinMoin/script/maint/serialization.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/script/maint/serialization.py	Sun Feb 10 19:55:14 2013 +0100
@@ -42,11 +42,34 @@
     option_list = [
         Option('--file', '-f', dest='filename', type=unicode, required=False,
                help='Filename of the output file.'),
+        Option('--backends', '-b', dest='backends', type=unicode, required=False,
+               help='Backend names to serialize (comma separated).'),
+        Option('--all-backends', '-a', dest='all_backends', action='store_true', default=False,
+               help='Serialize all configured backends.'),
     ]
 
-    def run(self, filename=None):
-        with open_file(filename, "wb") as f:
-            serialize(app.storage.backend, f)
+    def run(self, filename=None, backends=None, all_backends=False):
+        if filename is None:
+            f = sys.stdout
+        else:
+            f = open(filename, "wb")
+        with f as f:
+            existing_backends = set(app.cfg.backend_mapping)
+            if all_backends:
+                backends = set(app.cfg.backend_mapping)
+            elif backends:
+                backends = set(backends.split(','))
+            if backends:
+                # low level - directly serialize some backend contents -
+                # this does not use the index:
+                if backends.issubset(existing_backends):
+                    for backend_name in backends:
+                        backend = app.cfg.backend_mapping.get(backend_name)
+                        serialize(backend, f)
+                else:
+                    print "Error: Wrong backend name given."
+                    print "Given Backends: %r" % backends
+                    print "Configured Backends: %r" % existing_backends
 
 
 class Deserialize(Command):
--- a/MoinMoin/script/migration/moin19/import19.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/script/migration/moin19/import19.py	Sun Feb 10 19:55:14 2013 +0100
@@ -522,6 +522,9 @@
         # rename last_saved to MTIME, int MTIME should be enough:
         metadata[MTIME] = int(float(metadata.get('last_saved', '0')))
 
+        # rename aliasname to display_name:
+        metadata['display_name'] = metadata.get('aliasname')
+
         # rename subscribed_pages to subscribed_items
         metadata['subscribed_items'] = metadata.get('subscribed_pages', [])
 
@@ -530,7 +533,8 @@
                                  for interwiki, bookmark in metadata.get('bookmarks', {}).items()]
 
         # stuff we want to get rid of:
-        kill = ['real_language', # crap (use 'language')
+        kill = ['aliasname', # renamed to display_name
+                'real_language', # crap (use 'language')
                 'wikiname_add_spaces', # crap magic (you get it like it is)
                 'recoverpass_key', # user can recover again if needed
                 'editor_default', # not used any more
@@ -562,7 +566,7 @@
 
         # finally, remove some empty values (that have empty defaults anyway or
         # make no sense when empty):
-        empty_kill = ['aliasname', 'bookmarks', 'enc_password',
+        empty_kill = ['aliasname', 'display_name', 'bookmarks', 'enc_password',
                       'language', 'css_url', 'email', ] # XXX check subscribed_items, quicklinks
         for key in empty_kill:
             if key in metadata and metadata[key] in [u'', tuple(), {}, [], ]:
--- a/MoinMoin/security/__init__.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/security/__init__.py	Sun Feb 10 19:55:14 2013 +0100
@@ -64,7 +64,7 @@
             return super(MySecPol, self).read(itemname)
     """
     def __init__(self, user):
-        self.name = user.name
+        self.names = user.name
 
     def read(self, itemname):
         """read permission is special as we have 2 kinds of read capabilities:
@@ -72,9 +72,9 @@
            * READ - gives permission to read, unconditionally
            * PUBREAD - gives permission to read, when published
         """
-        return (flaskg.storage.may(itemname, rights.READ, username=self.name)
+        return (flaskg.storage.may(itemname, rights.READ, usernames=self.names)
                 or
-                flaskg.storage.may(itemname, rights.PUBREAD, username=self.name))
+                flaskg.storage.may(itemname, rights.PUBREAD, usernames=self.names))
 
     def __getattr__(self, attr):
         """ Shortcut to handle all known ACL rights.
@@ -87,10 +87,16 @@
         :returns: checking function for that right
         """
         if attr in app.cfg.acl_rights_contents:
-            return lambda itemname: flaskg.storage.may(itemname, attr, username=self.name)
+            return lambda itemname: flaskg.storage.may(itemname, attr, usernames=self.names)
         if attr in app.cfg.acl_rights_functions:
-            may = app.cfg.cache.acl_functions.may
-            return lambda: may(self.name, attr)
+            def multiuser_may():
+                # TODO: if "may" would accept multiple names, we could get rid of this
+                may = app.cfg.cache.acl_functions.may
+                for name in self.names:
+                    if may(name, attr):
+                        return True
+                return False
+            return multiuser_may
         raise AttributeError(attr)
 
 
@@ -248,7 +254,7 @@
                             handler = getattr(self, "_special_" + special, None)
                             allowed = handler(name, dowhat, rightsdict)
                             break # order of self.special_users is important
-            elif entry == name:
+            elif entry == name:  # XXX TODO maybe change this to "entry in names" to check users with multiple names in one go
                 allowed = rightsdict.get(dowhat)
             if allowed is not None:
                 return allowed
--- a/MoinMoin/security/_tests/test_security.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/security/_tests/test_security.py	Sun Feb 10 19:55:14 2013 +0100
@@ -15,7 +15,7 @@
 from MoinMoin.security import AccessControlList, ACLStringIterator
 
 from MoinMoin.user import User
-from MoinMoin.config import ACL
+from MoinMoin.config import NAME, ACL
 from MoinMoin.datastruct import ConfigGroups
 
 from MoinMoin._tests import update_item
@@ -423,7 +423,7 @@
             (self.mainitem_name, u'JaneDoe', ['read', 'write']), # by item acl
             (self.mainitem_name, u'JoeDoe', []), # by item acl
             (self.subitem1_name, u'WikiAdmin', ['read', 'write', 'admin', 'create', 'destroy']),
-            (self.subitem1_name, u'AnyUser', ['read', 'write']), # by default acl
+            (self.subitem1_name, u'AnyUser', ['read']), # by after acl
             (self.subitem1_name, u'JoeDoe', []), # by inherited acl from main item
             (self.subitem1_name, u'JaneDoe', ['read', 'write']), # by inherited acl from main item
             (self.subitem2_name, u'WikiAdmin', ['read', 'write', 'admin', 'create', 'destroy']),
@@ -457,4 +457,83 @@
                 yield _not_have_right, u, right, itemname
 
 
+class TestItemHierachicalAclsMultiItemNames(object):
+    """ security: real-life access control list on items testing
+    """
+    # parent / child item names
+    p1 = [u'p1', ]
+    c1 = [u'p1/c1', ]
+    p2 = [u'p2', ]
+    c2 = [u'p2/c2', ]
+    c12 = [u'p1/c12', u'p2/c12', ]
+    items = [
+        # itemnames, acl, content
+        (p1, u'Editor:', p1),  # deny access (due to hierarchic acl mode also effective for children)
+        (c1, None, c1),  # no own acl -> inherit from parent
+        (p2, None, p2),  # default acl effective (also for children)
+        (c2, None, c2),  # no own acl -> inherit from parent
+        (c12, None, c12),  # no own acl -> inherit from parents
+        ]
+
+    from MoinMoin._tests import wikiconfig
+
+    class Config(wikiconfig.Config):
+        content_acl = dict(hierarchic=True, before=u"WikiAdmin:admin,read,write,create,destroy", default=u"Editor:read,write", after=u"All:read")
+
+    def setup_method(self, method):
+        become_trusted(username=u'WikiAdmin')
+        for item_names, item_acl, item_content in self.items:
+            meta = {NAME: item_names}
+            if item_acl is not None:
+                meta.update({ACL: item_acl})
+            update_item(item_names[0], meta, item_content)
+
+    def testItemACLs(self):
+        """ security: test item acls """
+        tests = [
+            # itemname, username, expected_rights
+            (self.p1, u'WikiAdmin', ['read', 'write', 'admin', 'create', 'destroy']),  # by before acl
+            (self.p2, u'WikiAdmin', ['read', 'write', 'admin', 'create', 'destroy']),  # by before acl
+            (self.c1, u'WikiAdmin', ['read', 'write', 'admin', 'create', 'destroy']),  # by before acl
+            (self.c2, u'WikiAdmin', ['read', 'write', 'admin', 'create', 'destroy']),  # by before acl
+            (self.c12, u'WikiAdmin', ['read', 'write', 'admin', 'create', 'destroy']),  # by before acl
+            (self.p1, u'Editor', []),  # by p1 acl
+            (self.c1, u'Editor', []),  # by p1 acl
+            (self.p1, u'SomeOne', ['read']),  # by after acl
+            (self.c1, u'SomeOne', ['read']),  # by after acl
+            (self.p2, u'Editor', ['read', 'write']),  # by default acl
+            (self.c2, u'Editor', ['read', 'write']),  # by default acl
+            (self.p2, u'SomeOne', ['read']),  # by after acl
+            (self.c2, u'SomeOne', ['read']),  # by after acl
+            (self.c12, u'SomeOne', ['read']),  # by after acl
+            # now check the rather special stuff:
+            (self.c12, u'Editor', ['read', 'write']),  # disallowed via p1, but allowed via p2 via default acl
+        ]
+
+        for itemnames, username, may in tests:
+            u = User(auth_username=username)
+            u.valid = True
+            itemname = itemnames[0]
+
+            def _have_right(u, right, itemname):
+                can_access = getattr(u.may, right)(itemname)
+                assert can_access, "{0!r} may {1} {2!r} (hierarchic)".format(u.name, right, itemname)
+
+            # User should have these rights...
+            for right in may:
+                yield _have_right, u, right, itemname
+
+            def _not_have_right(u, right, itemname):
+                can_access = getattr(u.may, right)(itemname)
+                assert not can_access, "{0!r} may not {1} {2!r} (hierarchic)".format(u.name, right, itemname)
+
+            # User should NOT have these rights:
+            mayNot = [right for right in app.cfg.acl_rights_contents
+                      if right not in may]
+            for right in mayNot:
+                yield _not_have_right, u, right, itemname
+
+
+# XXX TODO add tests for a user having multiple usernames (one resulting in more permissions than other)
+
 coverage_modules = ['MoinMoin.security']
--- a/MoinMoin/storage/__init__.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/__init__.py	Sun Feb 10 19:55:14 2013 +0100
@@ -12,7 +12,7 @@
  |                                 listing, lookup by name, ACL checks, ...
  v
  Routing  Middleware               dispatches to multiple backends based on the
- |                 |               name, cares about absolute and relative names
+ |                 |               namespace
  v                 v
  "stores" Backend  Other Backend   simple stuff: store, get, destroy revisions
  |           |
@@ -22,7 +22,7 @@
 """
 
 
-CONTENT, USERPROFILES = 'content', 'userprofiles'
+CONTENT, USERPROFILES = u'content', u'userprofiles'
 
 BACKENDS_PACKAGE = 'MoinMoin.storage.backends'
 
@@ -39,15 +39,16 @@
     return module.MutableBackend.from_uri(backend_uri)
 
 
-def create_mapping(uri, mounts, acls):
-    namespace_mapping = [(mounts[nsname],
-                          backend_from_uri(uri % dict(nsname=nsname, kind="%(kind)s")))
-                         for nsname in mounts]
+def create_mapping(uri, namespaces, backends, acls):
+    namespace_mapping = namespaces.items()
     acl_mapping = acls.items()
+    backend_mapping = [(backend_name,
+                        backend_from_uri(uri % dict(backend=backend_name, kind="%(kind)s")))
+                        for backend_name in backends]
     # we need the longest mountpoints first, shortest last (-> '' is very last)
     namespace_mapping = sorted(namespace_mapping, key=lambda x: len(x[0]), reverse=True)
     acl_mapping = sorted(acl_mapping, key=lambda x: len(x[0]), reverse=True)
-    return namespace_mapping, acl_mapping
+    return namespace_mapping, dict(backend_mapping), acl_mapping
 
 def create_simple_mapping(uri='stores:fs:instance',
                           content_acl=None, user_profile_acl=None):
@@ -60,9 +61,9 @@
 
     :params uri: '<backend_name>:<backend_uri>' (general form)
                  backend_name must be a backend module name (e.g. stores)
-                 the backend_uri must have a %(nsname)s placeholder, it gets replaced
-                 by the CONTENT, USERPROFILES strings and result is given to
-                 to that backend's constructor
+                 the backend_uri must have a %(backend)s placeholder, it gets replaced
+                 by the name of the backend (a simple, ascii string) and result
+                 is given to to that backend's constructor
 
                  for the 'stores' backend, backend_uri looks like '<store_name>:<store_uri>'
                  store_name must be a store module name (e.g. fs)
@@ -70,7 +71,7 @@
                  by 'meta' or 'data' and the result is given to that store's constructor
 
                  e.g.:
-                 'stores:fs:/path/to/store/%(nsname)s/%(kind)s' will create a mapping
+                 'stores:fs:/path/to/store/%(backend)s/%(kind)s' will create a mapping
                  using the 'stores' backend with 'fs' stores and everything will be stored
                  to below /path/to/store/.
     """
@@ -79,12 +80,16 @@
         content_acl = dict(before=u'', default=u'All:read,write,create', after=u'', hierarchic=False)
     if not user_profile_acl:
         user_profile_acl = dict(before=u'All:', default=u'', after=u'', hierarchic=False)
-    mounts = {
-        CONTENT: '',
-        USERPROFILES: 'UserProfile',
+    namespaces = {
+        u'': CONTENT,
+        u'userprofiles:': USERPROFILES,
+    }
+    backends = {
+        CONTENT: None,
+        USERPROFILES: None,
     }
     acls = {
-        'UserProfile/': user_profile_acl,
+        'userprofiles:': user_profile_acl,
         '': content_acl,
     }
-    return create_mapping(uri, mounts, acls)
+    return create_mapping(uri, namespaces, backends, acls)
--- a/MoinMoin/storage/backends/stores.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/backends/stores.py	Sun Feb 10 19:55:14 2013 +0100
@@ -147,10 +147,15 @@
             dataid = meta[DATAID]
             # we will just asume stuff is correct if you pass it with a data id
             if dataid not in self.data_store:
-                # XXX issue: if we do not store if we already have the dataid in the store,
-                # XXX deserialization does not work as the fpos does not advance to the next record,
-                # XXX because we do not read from the source file. Remove the check?
                 self.data_store[dataid] = data
+            else:
+                # this is reading the data to avoid this issue:
+                # if we do not store if we already have the dataid in the store,
+                # deserialization does not work as the fpos does not advance to the next record,
+                # because we do not read from the source file. Remove the check?
+                while data.read(64*1024):
+                    pass
+
         # if something goes wrong below, the data shall be purged by a garbage collection
         metaid = self._store_meta(meta)
         return metaid
--- a/MoinMoin/storage/middleware/_tests/test_indexing.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/_tests/test_indexing.py	Sun Feb 10 19:55:14 2013 +0100
@@ -16,7 +16,7 @@
 from flask import g as flaskg
 
 from MoinMoin.config import NAME, SIZE, ITEMID, REVID, DATAID, HASH_ALGORITHM, CONTENT, COMMENT, \
-                            LATEST_REVS, ALL_REVS
+                            LATEST_REVS, ALL_REVS, NAMESPACE
 
 from ..indexing import IndexingMiddleware
 
@@ -26,7 +26,6 @@
 from MoinMoin.storage.stores.memory import BytesStore as MemoryBytesStore
 from MoinMoin.storage.stores.memory import FileStore as MemoryFileStore
 from MoinMoin.storage import create_simple_mapping
-from MoinMoin.storage.middleware import routing
 
 
 def dumper(indexer, idx_name):
@@ -54,14 +53,14 @@
         item_name = u'foo'
         data = 'bar'
         item = self.imw[item_name]
-        rev = item.store_revision(dict(name=item_name), StringIO(data),
+        rev = item.store_revision(dict(name=[item_name, ]), StringIO(data),
                                   return_rev=True)
         revid = rev.revid
         # check if we have the revision now:
         item = self.imw[item_name]
         assert item # does exist
         rev = item.get_revision(revid)
-        assert rev.meta[NAME] == item_name
+        assert rev.name == item_name
         assert rev.data.read() == data
         revids = [rev.revid for rev in item.iter_revs()]
         assert revids == [revid]
@@ -71,15 +70,15 @@
         data = 'bar'
         newdata = 'baz'
         item = self.imw[item_name]
-        rev = item.store_revision(dict(name=item_name, comment=u'spam'), StringIO(data),
+        rev = item.store_revision(dict(name=[item_name, ], comment=u'spam'), StringIO(data),
                                   return_rev=True)
         revid = rev.revid
         # clear revision:
-        item.store_revision(dict(name=item_name, revid=revid, comment=u'no spam'), StringIO(newdata), overwrite=True)
+        item.store_revision(dict(name=[item_name, ], revid=revid, comment=u'no spam'), StringIO(newdata), overwrite=True)
         # check if the revision was overwritten:
         item = self.imw[item_name]
         rev = item.get_revision(revid)
-        assert rev.meta[NAME] == item_name
+        assert rev.name == item_name
         assert rev.meta[COMMENT] == u'no spam'
         assert rev.data.read() == newdata
         revids = [rev.revid for rev in item.iter_revs()]
@@ -89,13 +88,13 @@
     def test_destroy_revision(self):
         item_name = u'foo'
         item = self.imw[item_name]
-        rev = item.store_revision(dict(name=item_name, mtime=1),
+        rev = item.store_revision(dict(name=[item_name, ], mtime=1),
                                   StringIO('bar'), trusted=True, return_rev=True)
         revid0 = rev.revid
-        rev = item.store_revision(dict(name=item_name, mtime=2),
+        rev = item.store_revision(dict(name=[item_name, ], mtime=2),
                                   StringIO('baz'), trusted=True, return_rev=True)
         revid1 = rev.revid
-        rev = item.store_revision(dict(name=item_name, mtime=3),
+        rev = item.store_revision(dict(name=[item_name, ], mtime=3),
                                   StringIO('...'), trusted=True, return_rev=True)
         revid2 = rev.revid
         print "revids:", revid0, revid1, revid2
@@ -131,10 +130,10 @@
         revids = []
         item_name = u'foo'
         item = self.imw[item_name]
-        rev = item.store_revision(dict(name=item_name, mtime=1),
+        rev = item.store_revision(dict(name=[item_name, ], mtime=1),
                                   StringIO('bar'), trusted=True, return_rev=True)
         revids.append(rev.revid)
-        rev = item.store_revision(dict(name=item_name, mtime=2),
+        rev = item.store_revision(dict(name=[item_name, ], mtime=2),
                                   StringIO('baz'), trusted=True, return_rev=True)
         revids.append(rev.revid)
         # destroy item:
@@ -146,11 +145,11 @@
     def test_all_revisions(self):
         item_name = u'foo'
         item = self.imw[item_name]
-        item.store_revision(dict(name=item_name), StringIO('does not count, different name'))
+        item.store_revision(dict(name=[item_name, ]), StringIO('does not count, different name'))
         item_name = u'bar'
         item = self.imw[item_name]
-        item.store_revision(dict(name=item_name), StringIO('1st'))
-        item.store_revision(dict(name=item_name), StringIO('2nd'))
+        item.store_revision(dict(name=[item_name, ]), StringIO('1st'))
+        item.store_revision(dict(name=[item_name, ]), StringIO('2nd'))
         item = self.imw[item_name]
         revs = [rev.data.read() for rev in item.iter_revs()]
         assert len(revs) == 2
@@ -159,11 +158,11 @@
     def test_latest_revision(self):
         item_name = u'foo'
         item = self.imw[item_name]
-        item.store_revision(dict(name=item_name), StringIO('does not count, different name'))
+        item.store_revision(dict(name=[item_name, ]), StringIO('does not count, different name'))
         item_name = u'bar'
         item = self.imw[item_name]
-        item.store_revision(dict(name=item_name), StringIO('1st'))
-        expected_rev = item.store_revision(dict(name=item_name), StringIO('2nd'),
+        item.store_revision(dict(name=[item_name, ]), StringIO('1st'))
+        expected_rev = item.store_revision(dict(name=[item_name, ]), StringIO('2nd'),
                                            return_rev=True)
         revs = list(self.imw.documents(name=item_name))
         assert len(revs) == 1  # there is only 1 latest revision
@@ -173,9 +172,9 @@
         item_name = u'foo'
         data = 'bar'
         item = self.imw[item_name]
-        rev = item.store_revision(dict(name=item_name), StringIO(data), return_rev=True)
+        rev = item.store_revision(dict(name=[item_name, ]), StringIO(data), return_rev=True)
         print repr(rev.meta)
-        assert rev.meta[NAME] == item_name
+        assert rev.name == item_name
         assert rev.meta[SIZE] == len(data)
         assert rev.meta[HASH_ALGORITHM] == hashlib.new(HASH_ALGORITHM, data).hexdigest()
         assert ITEMID in rev.meta
@@ -185,9 +184,9 @@
     def test_documents(self):
         item_name = u'foo'
         item = self.imw[item_name]
-        rev1 = item.store_revision(dict(name=item_name), StringIO('x'), return_rev=True)
-        rev2 = item.store_revision(dict(name=item_name), StringIO('xx'), return_rev=True)
-        rev3 = item.store_revision(dict(name=item_name), StringIO('xxx'), return_rev=True)
+        rev1 = item.store_revision(dict(name=[item_name, ]), StringIO('x'), return_rev=True)
+        rev2 = item.store_revision(dict(name=[item_name, ]), StringIO('xx'), return_rev=True)
+        rev3 = item.store_revision(dict(name=[item_name, ]), StringIO('xxx'), return_rev=True)
         rev = self.imw.document(idx_name=ALL_REVS, size=2)
         assert rev
         assert rev.revid == rev2.revid
@@ -200,15 +199,15 @@
         expected_latest_revids = []
         item_name = u'foo'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, mtime=1),
+        r = item.store_revision(dict(name=[item_name, ], mtime=1),
                                 StringIO('does not count, different name'),
                                 trusted=True, return_rev=True)
         expected_latest_revids.append(r.revid)
         item_name = u'bar'
         item = self.imw[item_name]
-        item.store_revision(dict(name=item_name, mtime=1),
+        item.store_revision(dict(name=[item_name, ], mtime=1),
                             StringIO('1st'), trusted=True)
-        r = item.store_revision(dict(name=item_name, mtime=2),
+        r = item.store_revision(dict(name=[item_name, ], mtime=2),
                                 StringIO('2nd'), trusted=True, return_rev=True)
         expected_latest_revids.append(r.revid)
 
@@ -250,14 +249,14 @@
         missing_revids = []
         item_name = u'updated'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, mtime=1),
+        r = item.store_revision(dict(name=[item_name, ], mtime=1),
                                 StringIO('updated 1st'),
                                 trusted=True, return_rev=True)
         expected_all_revids.append(r.revid)
         # we update this item below, so we don't add it to expected_latest_revids
         item_name = u'destroyed'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, mtime=1),
+        r = item.store_revision(dict(name=[item_name, ], mtime=1),
                                 StringIO('destroyed 1st'),
                                 trusted=True, return_rev=True)
         destroy_revid = r.revid
@@ -265,12 +264,12 @@
         # we destroy this item below, so we don't add it to expected_latest_revids
         item_name = u'stayssame'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, mtime=1),
+        r = item.store_revision(dict(name=[item_name, ], mtime=1),
                                 StringIO('stayssame 1st'),
                                 trusted=True, return_rev=True)
         expected_all_revids.append(r.revid)
         # we update this item below, so we don't add it to expected_latest_revids
-        r = item.store_revision(dict(name=item_name, mtime=2),
+        r = item.store_revision(dict(name=[item_name, ], mtime=2),
                                 StringIO('stayssame 2nd'),
                                 trusted=True, return_rev=True)
         expected_all_revids.append(r.revid)
@@ -287,7 +286,7 @@
         # this will not change the fresh index, but the old index we are still using.
         item_name = u'updated'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, mtime=2),
+        r = item.store_revision(dict(name=[item_name, ], mtime=2),
                                 StringIO('updated 2nd'), trusted=True,
                                 return_rev=True)
         expected_all_revids.append(r.revid)
@@ -295,7 +294,7 @@
         missing_revids.append(r.revid)
         item_name = u'added'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, mtime=1),
+        r = item.store_revision(dict(name=[item_name, ], mtime=1),
                                 StringIO('added 1st'),
                                 trusted=True, return_rev=True)
         expected_all_revids.append(r.revid)
@@ -344,7 +343,7 @@
     def test_revision_contextmanager(self):
         # check if rev.data is closed after leaving the with-block
         item_name = u'foo'
-        meta = dict(name=item_name)
+        meta = dict(name=[item_name, ])
         data = 'some test content'
         item = self.imw[item_name]
         data_file = StringIO(data)
@@ -362,7 +361,7 @@
         # TODO: this is a very simple check that assumes that data is put 1:1
         # into index' CONTENT field.
         item_name = u'foo'
-        meta = dict(name=item_name, contenttype=u'text/plain')
+        meta = dict(name=[item_name, ], contenttype=u'text/plain')
         data = 'some test content\n'
         item = self.imw[item_name]
         data_file = StringIO(data)
@@ -373,6 +372,33 @@
         assert expected_revid == doc[REVID]
         assert unicode(data) == doc[CONTENT]
 
+    def test_namespaces(self):
+        item_name_n = u'normal'
+        item = self.imw[item_name_n]
+        rev_n = item.store_revision(dict(name=[item_name_n, ], contenttype=u'text/plain'),
+                                    StringIO(str(item_name_n)), return_rev=True)
+        item_name_u = u'userprofiles:userprofile'
+        item = self.imw[item_name_u]
+        rev_u = item.store_revision(dict(name=[item_name_u, ], contenttype=u'text/plain'),
+                                    StringIO(str(item_name_u)), return_rev=True)
+        item = self.imw[item_name_n]
+        rev_n = item.get_revision(rev_n.revid)
+        assert rev_n.meta[NAMESPACE] == u''
+        assert rev_n.meta[NAME] == [item_name_n, ]
+        item = self.imw[item_name_u]
+        rev_u = item.get_revision(rev_u.revid)
+        assert rev_u.meta[NAMESPACE] == u'userprofiles'
+        assert rev_u.meta[NAME] == [item_name_u.split(':')[1]]
+
+    def test_parentnames(self):
+        item_name = u'child'
+        item = self.imw[item_name]
+        item.store_revision(dict(name=[u'child', u'p1/a', u'p2/b', u'p2/c', u'p3/p4/d', ],
+                                 contenttype=u'text/plain'),
+                            StringIO(''))
+        item = self.imw[item_name]
+        assert item.parentnames == [u'p1', u'p2', u'p3/p4', ]  # one p2 duplicate removed
+
 class TestProtectedIndexingMiddleware(object):
     reinit_storage = True # cleanup after each test method
 
@@ -388,17 +414,17 @@
     def test_documents(self):
         item_name = u'public'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, acl=u'joe:read'),
+        r = item.store_revision(dict(name=[item_name, ], acl=u'joe:read'),
                                 StringIO('public content'), return_rev=True)
         revid_public = r.revid
         revids = [rev.revid for rev in self.imw.documents()
-                  if rev.meta[NAME] != u'joe'] # the user profile is a revision in the backend
+                  if rev.name != u'joe'] # the user profile is a revision in the backend
         assert revids == [revid_public]
 
     def test_getitem(self):
         item_name = u'public'
         item = self.imw[item_name]
-        r = item.store_revision(dict(name=item_name, acl=u'joe:read'),
+        r = item.store_revision(dict(name=[item_name, ], acl=u'joe:read'),
                                 StringIO('public content'), return_rev=True)
         revid_public = r.revid
         # now testing:
@@ -414,7 +440,7 @@
         item_name = u'foo'
         item = self.imw[item_name]
         for i in xrange(100):
-            item.store_revision(dict(name=item_name, acl=u'joe:create joe:read'), StringIO('some content'))
+            item.store_revision(dict(name=[item_name, ], acl=u'joe:create joe:read'), StringIO('some content'))
 
     def test_perf_create_read(self):
         pytest.skip("usually we do no performance tests")
@@ -424,7 +450,7 @@
         item_name = u'foo'
         item = self.imw[item_name]
         for i in xrange(100):
-            item.store_revision(dict(name=item_name, acl=u'joe:create joe:read'), StringIO('rev number {0}'.format(i)))
+            item.store_revision(dict(name=[item_name, ], acl=u'joe:create joe:read'), StringIO('rev number {0}'.format(i)))
         for r in item.iter_revs():
             #print r.meta
             #print r.data.read()
--- a/MoinMoin/storage/middleware/_tests/test_protecting.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/_tests/test_protecting.py	Sun Feb 10 19:55:14 2013 +0100
@@ -28,17 +28,21 @@
     ('', dict(before=u'', default=u'All:read,write,create', after=u'', hierarchic=False)),
 ]
 
-class User(object):
+class FakeUser(object):
     """
     fake user object, just to give user.name
     """
     def __init__(self, name):
-        self.name = name
+        self.name = [name, ]
+    @property
+    def name0(self):
+        return self.name[0]
+
 
 class TestProtectingMiddleware(TestIndexingMiddleware):
     def setup_method(self, method):
         super(TestProtectingMiddleware, self).setup_method(method)
-        self.imw = ProtectingMiddleware(self.imw, User(u'joe'), acl_mapping=acl_mapping)
+        self.imw = ProtectingMiddleware(self.imw, FakeUser(u'joe'), acl_mapping=acl_mapping)
 
     def teardown_method(self, method):
         self.imw = self.imw.indexer
@@ -59,7 +63,7 @@
         revids = []
         for item_name, acl, content in items:
             item = self.imw[item_name]
-            r = item.store_revision(dict(name=item_name, acl=acl),
+            r = item.store_revision(dict(name=[item_name, ], acl=acl, contenttype=u'text/plain'),
                                     StringIO(content), return_rev=True)
             revids.append(r.revid)
         return revids
@@ -83,26 +87,26 @@
         revid_unprotected, revid_protected = self.make_items(u'joe:write', u'boss:write')
         # now testing:
         item = self.imw[UNPROTECTED]
-        item.store_revision(dict(name=UNPROTECTED, acl=u'joe:write'), StringIO(UNPROTECTED_CONTENT))
+        item.store_revision(dict(name=[UNPROTECTED, ], acl=u'joe:write', contenttype=u'text/plain'), StringIO(UNPROTECTED_CONTENT))
         item = self.imw[PROTECTED]
         with pytest.raises(AccessDenied):
-            item.store_revision(dict(name=PROTECTED, acl=u'boss:write'), StringIO(UNPROTECTED_CONTENT))
+            item.store_revision(dict(name=[PROTECTED, ], acl=u'boss:write', contenttype=u'text/plain'), StringIO(UNPROTECTED_CONTENT))
 
     def test_write_create(self):
         # now testing:
         item_name = u'newitem'
         item = self.imw[item_name]
-        item.store_revision(dict(name=item_name), StringIO('new content'))
+        item.store_revision(dict(name=[item_name, ], contenttype=u'text/plain'), StringIO('new content'))
 
     def test_overwrite_revision(self):
         revid_unprotected, revid_protected = self.make_items(u'joe:write,destroy', u'boss:write,destroy')
         # now testing:
         item = self.imw[UNPROTECTED]
-        item.store_revision(dict(name=UNPROTECTED, acl=u'joe:write,destroy', revid=revid_unprotected),
+        item.store_revision(dict(name=[UNPROTECTED, ], acl=u'joe:write,destroy', contenttype=u'text/plain', revid=revid_unprotected),
                             StringIO(UNPROTECTED_CONTENT), overwrite=True)
         item = self.imw[PROTECTED]
         with pytest.raises(AccessDenied):
-            item.store_revision(dict(name=PROTECTED, acl=u'boss:write,destroy', revid=revid_protected),
+            item.store_revision(dict(name=[PROTECTED, ], acl=u'boss:write,destroy', contenttype=u'text/plain', revid=revid_protected),
                                 StringIO(UNPROTECTED_CONTENT), overwrite=True)
 
     def test_destroy_revision(self):
--- a/MoinMoin/storage/middleware/_tests/test_routing.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/_tests/test_routing.py	Sun Feb 10 19:55:14 2013 +0100
@@ -2,7 +2,7 @@
 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
 
 """
-MoinMoin - router middleware tests
+MoinMoin - routing middleware tests
 """
 
 
@@ -12,9 +12,9 @@
 
 import pytest
 
-from MoinMoin.config import NAME, REVID
+from MoinMoin.config import NAME, NAMESPACE, REVID
 
-from ..routing import Backend as RouterBackend
+from ..routing import Backend as RoutingBackend
 
 from MoinMoin.storage.backends.stores import MutableBackend as StoreBackend, Backend as ROBackend
 from MoinMoin.storage.stores.memory import BytesStore as MemoryBytesStore
@@ -31,10 +31,12 @@
 
 
 def pytest_funcarg__router(request):
-    root_be = StoreBackend(MemoryBytesStore(), MemoryFileStore())
-    sub_be = StoreBackend(MemoryBytesStore(), MemoryFileStore())
+    default_be = StoreBackend(MemoryBytesStore(), MemoryFileStore())
+    other_be = StoreBackend(MemoryBytesStore(), MemoryFileStore())
     ro_be = make_ro_backend()
-    router = RouterBackend([('sub', sub_be), ('ro', ro_be), ('', root_be)])
+    namespaces = [(u'other:', 'other'), (u'ro:', 'ro'), (u'', 'default')]
+    backends = {'other': other_be, 'ro': ro_be, 'default': default_be, }
+    router = RoutingBackend(namespaces, backends)
     router.create()
     router.open()
 
@@ -46,42 +48,39 @@
     return router
 
 def test_store_get_del(router):
-    root_name = u'foo'
-    root_revid = router.store(dict(name=root_name), StringIO(''))
-    sub_name = u'sub/bar'
-    sub_revid = router.store(dict(name=sub_name), StringIO(''))
+    default_name = u'foo'
+    default_backend_name, default_revid = router.store(dict(name=[default_name, ]), StringIO(''))
+    other_name = u'other:bar'
+    other_backend_name, other_revid = router.store(dict(name=[other_name, ]), StringIO(''))
 
-    # when going via the router backend, we get back fully qualified names:
-    root_meta, _ = router.retrieve(root_name, root_revid)
-    sub_meta, _ = router.retrieve(sub_name, sub_revid)
-    assert root_name == root_meta[NAME]
-    assert sub_name == sub_meta[NAME]
+    # check if store() updates the to-store metadata with correct NAMESPACE and NAME
+    default_meta, _ = router.retrieve(default_backend_name, default_revid)
+    other_meta, _ = router.retrieve(other_backend_name, other_revid)
+    assert u'' == default_meta[NAMESPACE]
+    assert [default_name, ] == default_meta[NAME]
+    assert other_name.split(':')[0] == other_meta[NAMESPACE]
+    assert other_name.split(':')[1] == other_meta[NAME][0]
 
-    # when looking into the storage backend, we see relative names (without mountpoint):
-    root_meta, _ = router.mapping[-1][1].retrieve(root_revid)
-    sub_meta, _ = router.mapping[0][1].retrieve(sub_revid)
-    assert root_name == root_meta[NAME]
-    assert sub_name == 'sub' + '/' + sub_meta[NAME]
     # delete revs:
-    router.remove(root_name, root_revid)
-    router.remove(sub_name, sub_revid)
+    router.remove(default_backend_name, default_revid)
+    router.remove(other_backend_name, other_revid)
 
 
 def test_store_readonly_fails(router):
     with pytest.raises(TypeError):
-        router.store(dict(name=u'ro/testing'), StringIO(''))
+        router.store(dict(name=[u'ro:testing', ]), StringIO(''))
 
 def test_del_readonly_fails(router):
-    ro_id = next(iter(router)) # we have only readonly items
-    print ro_id
+    ro_be_name, ro_id = next(iter(router)) # we have only readonly items
+    print ro_be_name, ro_id
     with pytest.raises(TypeError):
-        router.remove(ro_id)
+        router.remove(ro_be_name, ro_id)
 
 
 def test_destroy_create_dont_touch_ro(router):
     existing = set(router)
-    root_revid = router.store(dict(name=u'foo'), StringIO(''))
-    sub_revid = router.store(dict(name=u'sub/bar'), StringIO(''))
+    default_be_name, default_revid = router.store(dict(name=[u'foo', ]), StringIO(''))
+    other_be_name, other_revid = router.store(dict(name=[u'other:bar', ]), StringIO(''))
 
     router.close()
     router.destroy()
@@ -92,8 +91,8 @@
 
 
 def test_iter(router):
-    existing_before = set([revid for mountpoint, revid in router])
-    root_revid = router.store(dict(name=u'foo'), StringIO(''))
-    sub_revid = router.store(dict(name=u'sub/bar'), StringIO(''))
-    existing_now = set([revid for mountpoint, revid in router])
-    assert existing_now == set([root_revid, sub_revid]) | existing_before
+    existing_before = set([revid for be_name, revid in router])
+    default_be_name, default_revid = router.store(dict(name=[u'foo', ]), StringIO(''))
+    other_be_name, other_revid = router.store(dict(name=[u'other:bar', ]), StringIO(''))
+    existing_now = set([revid for be_name, revid in router])
+    assert existing_now == set([default_revid, other_revid]) | existing_before
--- a/MoinMoin/storage/middleware/_tests/test_serialization.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/_tests/test_serialization.py	Sun Feb 10 19:55:14 2013 +0100
@@ -19,11 +19,11 @@
 
 
 contents = [
-    (u'Foo', {'name': u'Foo'}, ''),
-    (u'Foo', {'name': u'Foo'}, '2nd'),
-    (u'Subdir', {'name': u'Subdir'}, ''),
-    (u'Subdir/Foo', {'name': u'Subdir/Foo'}, ''),
-    (u'Subdir/Bar', {'name': u'Subdir/Bar'}, ''),
+    (u'Foo', {'name': [u'Foo', ], 'contenttype': u'text/plain'}, ''),
+    (u'Foo', {'name': [u'Foo', ], 'contenttype': u'text/plain'}, '2nd'),
+    (u'Subdir', {'name': [u'Subdir', ], 'contenttype': u'text/plain'}, ''),
+    (u'Subdir/Foo', {'name': [u'Subdir/Foo', ], 'contenttype': u'text/plain'}, ''),
+    (u'Subdir/Bar', {'name': [u'Subdir/Bar', ], 'contenttype': u'text/plain'}, ''),
 ]
 
 
@@ -51,8 +51,9 @@
     meta_store = BytesStore()
     data_store = FileStore()
     _backend = MutableBackend(meta_store, data_store)
-    mapping = [('', _backend)]
-    backend = RoutingBackend(mapping)
+    namespaces = [('', u'backend')]
+    backends = {u'backend': _backend}
+    backend = RoutingBackend(namespaces, backends)
     backend.create()
     backend.open()
     request.addfinalizer(backend.destroy)
@@ -70,7 +71,7 @@
 def test_serialize_deserialize(source, target):
     i = 0
     for name, meta, data in contents:
-        item = source['name']
+        item = source[u'name']
         item.store_revision(dict(meta, mtime=i), StringIO(data))
         i += 1
 
--- a/MoinMoin/storage/middleware/_tests/test_validation.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/_tests/test_validation.py	Sun Feb 10 19:55:14 2013 +0100
@@ -31,7 +31,8 @@
         meta = {
             keys.REVID: make_uuid(),
             keys.PARENTID: make_uuid(),
-            keys.NAME: u"a",
+            keys.NAME: [u"a", ],
+            keys.NAMESPACE: u"",
             keys.ACL: u"All:read",
             keys.TAGS: [u"foo", u"bar"],
         }
@@ -45,6 +46,7 @@
                  keys.HASH_ALGORITHM: u'b9064b9a5efd8c6cef2d38a8169a0e1cbfdb41ba',
                  keys.SIZE: 0,
                  keys.WIKINAME: u'ThisWiki',
+                 keys.NAMESPACE: u'',
                  'rev_parent': rev,
                  'acl_parent': u"All:read",
                  'contenttype_current': u'text/x.moin.wiki;charset=utf-8',
@@ -64,7 +66,8 @@
         meta = {
             keys.ITEMID: make_uuid(),
             keys.REVID: make_uuid(),
-            keys.NAME: u"user name",
+            keys.NAME: [u"user name", ],
+            keys.NAMESPACE: u"userprofiles",
             keys.EMAIL: u"foo@example.org",
         }
 
@@ -74,6 +77,7 @@
                  keys.HOSTNAME: u'localhost',
                  keys.ADDRESS: u'127.0.0.1',
                  keys.WIKINAME: u'ThisWiki',
+                 keys.NAMESPACE: u'',
                 }
 
         m = UserMetaSchema(meta)
--- a/MoinMoin/storage/middleware/indexing.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/indexing.py	Sun Feb 10 19:55:14 2013 +0100
@@ -56,14 +56,15 @@
 import itertools
 import time
 import datetime
-from StringIO import StringIO
+
+from MoinMoin import log
+logging = log.getLogger(__name__)
 
 from flask import request
 from flask import g as flaskg
 from flask import current_app as app
 
 from whoosh.fields import Schema, TEXT, ID, IDLIST, NUMERIC, DATETIME, KEYWORD, BOOLEAN
-from whoosh.index import EmptyIndexError
 from whoosh.writing import AsyncWriter
 from whoosh.qparser import QueryParser, MultifieldParser, RegexPlugin, \
                            PseudoFieldPlugin
@@ -74,12 +75,12 @@
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
-from MoinMoin.config import WIKINAME, NAME, NAME_EXACT, MTIME, CONTENTTYPE, TAGS, \
+from MoinMoin.config import WIKINAME, NAMESPACE, NAME, NAME_EXACT, MTIME, CONTENTTYPE, TAGS, \
                             LANGUAGE, USERID, ADDRESS, HOSTNAME, SIZE, ACTION, COMMENT, SUMMARY, \
                             CONTENT, EXTERNALLINKS, ITEMLINKS, ITEMTRANSCLUSIONS, ACL, EMAIL, OPENID, \
                             ITEMID, REVID, CURRENT, PARENTID, \
                             PTIME, \
-                            LATEST_REVS, ALL_REVS, \
+                            LATEST_REVS, ALL_REVS, BACKENDNAME, \
                             CONTENTTYPE_USER
 from MoinMoin.constants import keys
 from MoinMoin.constants.keys import ITEMTYPE
@@ -87,7 +88,6 @@
 from MoinMoin import user
 from MoinMoin.search.analyzers import item_name_analyzer, MimeTokenizer, AclTokenizer
 from MoinMoin.themes import utctimestamp
-from MoinMoin.util.crypto import make_uuid
 from MoinMoin.storage.middleware.validation import ContentMetaSchema, UserMetaSchema
 from MoinMoin.storage.error import NoSuchItemError, ItemAlreadyExistsError
 
@@ -97,7 +97,40 @@
 INDEXES = [LATEST_REVS, ALL_REVS, ]
 
 
-def backend_to_index(meta, content, schema, wikiname):
+def get_names(meta):
+    """
+    Get the (list of) names from meta data and deal with misc. bad things that
+    can happen then (while not all code is fixed to do it correctly).
+
+    TODO make sure meta[NAME] is always a list of unicode
+
+    :param meta: a metadata dictionary that might have a NAME key
+    :return: list of names
+    """
+    msg = "NAME is not a list but %r - fix this! Workaround enabled."
+    names = meta.get(NAME)
+    if names is None:
+        logging.warning(msg % names)
+        names = []
+    elif isinstance(names, str):
+        logging.warning(msg % names)
+        names = [names.decode('utf-8'), ]
+    elif isinstance(names, unicode):
+        logging.warning(msg % names)
+        names = [names, ]
+    elif isinstance(names, tuple):
+        logging.warning(msg % names)
+        names = list(names)
+    elif not isinstance(names, list):
+        raise TypeError("NAME is not a list but %r - fix this!" % names)
+    if not names:
+        # we currently never return an empty list, some code
+        # might not be able to deal with it:
+        names = [u'DoesNotExist', ]
+    return names
+
+
+def backend_to_index(meta, content, schema, wikiname, backend_name):
     """
     Convert backend metadata/data to a whoosh document.
 
@@ -117,6 +150,7 @@
     doc[NAME_EXACT] = doc[NAME]
     doc[WIKINAME] = wikiname
     doc[CONTENT] = content
+    doc[BACKENDNAME] = backend_name
     return doc
 
 
@@ -125,7 +159,7 @@
 from MoinMoin.converter import default_registry
 from MoinMoin.util.iri import Iri
 
-def convert_to_indexable(meta, data, is_new=False):
+def convert_to_indexable(meta, data, item_name=None, is_new=False):
     """
     Convert revision data to a indexable content.
 
@@ -147,7 +181,7 @@
             class PseudoItem(object):
                 def __init__(self, name):
                     self.name = name
-            self.item = PseudoItem(meta.get(NAME))
+            self.item = PseudoItem(item_name)
         def read(self, *args, **kw):
             return self.data.read(*args, **kw)
         def seek(self, *args, **kw):
@@ -155,6 +189,9 @@
         def tell(self, *args, **kw):
             return self.data.tell(*args, **kw)
 
+    if not item_name:
+        item_name = get_names(meta)[0]
+
     rev = PseudoRev(meta, data)
     try:
         # TODO use different converter mode?
@@ -185,7 +222,7 @@
             # transclusions.
             if is_new:
                 # we only can modify new, uncommitted revisions, not stored revs
-                i = Iri(scheme='wiki', authority='', path='/' + meta[NAME])
+                i = Iri(scheme='wiki', authority='', path='/' + item_name)
                 doc.set(moin_page.page_href, unicode(i))
                 refs_conv(doc)
                 # side effect: we update some metadata:
@@ -197,7 +234,7 @@
         # no way
         raise TypeError("No converter for {0} --> {1}".format(input_contenttype, output_contenttype))
     except Exception as e: # catch all exceptions, we don't want to break an indexing run
-        logging.exception("Exception happened in conversion of item {0!r} rev {1} contenttype {2}:".format(meta[NAME], meta.get(REVID, 'new'), meta.get(CONTENTTYPE, '')))
+        logging.exception("Exception happened in conversion of item {0!r} rev {1} contenttype {2}:".format(item_name, meta.get(REVID, 'new'), meta.get(CONTENTTYPE, '')))
         doc = u'ERROR [{0!s}]'.format(e)
         return doc
 
@@ -226,6 +263,8 @@
             REVID: ID(unique=True, stored=True),
             # parent revision id
             PARENTID: ID(stored=True),
+            # backend name (which backend is this rev stored in?)
+            BACKENDNAME: ID(stored=True),
             # MTIME from revision metadata (converted to UTC datetime)
             MTIME: DATETIME(stored=True),
             # publish time from metadata (converted to UTC datetime)
@@ -405,7 +444,7 @@
             index_dir, index_dir_tmp = params[0], params_tmp[0]
             os.rename(index_dir_tmp, index_dir)
 
-    def index_revision(self, meta, content, async=True):
+    def index_revision(self, meta, content, backend_name, async=False): # True
         """
         Index a single revision, add it to all-revs and latest-revs index.
 
@@ -413,14 +452,14 @@
         :param content: preprocessed (filtered) indexable content
         :param async: if True, use the AsyncWriter, otherwise use normal writer
         """
-        doc = backend_to_index(meta, content, self.schemas[ALL_REVS], self.wikiname)
+        doc = backend_to_index(meta, content, self.schemas[ALL_REVS], self.wikiname, backend_name)
         if async:
             writer = AsyncWriter(self.ix[ALL_REVS])
         else:
             writer = self.ix[ALL_REVS].writer()
         with writer as writer:
             writer.update_document(**doc) # update, because store_revision() may give us an existing revid
-        doc = backend_to_index(meta, content, self.schemas[LATEST_REVS], self.wikiname)
+        doc = backend_to_index(meta, content, self.schemas[LATEST_REVS], self.wikiname, backend_name)
         if async:
             writer = AsyncWriter(self.ix[LATEST_REVS])
         else:
@@ -450,20 +489,20 @@
                     itemid = searcher.stored_fields(docnum_remove)[ITEMID]
             if docnum_remove is not None:
                 # we are removing a revid that is in latest revs index
-                latest_names_revids = self._find_latest_names_revids(self.ix[ALL_REVS], Term(ITEMID, itemid))
-                if latest_names_revids:
+                latest_backends_revids = self._find_latest_backends_revids(self.ix[ALL_REVS], Term(ITEMID, itemid))
+                if latest_backends_revids:
                     # we have a latest revision, just update the document in the index:
-                    assert len(latest_names_revids) == 1 # this item must have only one latest revision
-                    latest_name_revid = latest_names_revids[0]
+                    assert len(latest_backends_revids) == 1 # this item must have only one latest revision
+                    latest_backend_revid = latest_backends_revids[0]
                     # we must fetch from backend because schema for LATEST_REVS is different than for ALL_REVS
                     # (and we can't be sure we have all fields stored, too)
-                    meta, _ = self.backend.retrieve(*latest_name_revid)
+                    meta, _ = self.backend.retrieve(*latest_backend_revid)
                     # we only use meta (not data), because we do not want to transform data->content again (this
                     # is potentially expensive) as we already have the transformed content stored in ALL_REVS index:
                     with self.ix[ALL_REVS].searcher() as searcher:
-                        doc = searcher.document(revid=latest_name_revid[1])
+                        doc = searcher.document(revid=latest_backend_revid[1])
                         content = doc[CONTENT]
-                    doc = backend_to_index(meta, content, self.schemas[LATEST_REVS], self.wikiname)
+                    doc = backend_to_index(meta, content, self.schemas[LATEST_REVS], self.wikiname, backend_name=latest_backend_revid[0])
                     writer.update_document(**doc)
                 else:
                     # this is no revision left in this item that could be the new "latest rev", just kill the rev
@@ -477,11 +516,11 @@
               documents in the index.
         """
         with index.writer(procs=procs, limitmb=limitmb) as writer:
-            for mountpoint, revid in revids:
+            for backend_name, revid in revids:
                 if mode in ['add', 'update', ]:
-                    meta, data = self.backend.retrieve(mountpoint, revid)
+                    meta, data = self.backend.retrieve(backend_name, revid)
                     content = convert_to_indexable(meta, data, is_new=False)
-                    doc = backend_to_index(meta, content, schema, wikiname)
+                    doc = backend_to_index(meta, content, schema, wikiname, backend_name)
                 if mode == 'update':
                     writer.update_document(**doc)
                 elif mode == 'add':
@@ -491,13 +530,13 @@
                 else:
                     raise ValueError("mode must be 'update', 'add' or 'delete', not '{0}'".format(mode))
 
-    def _find_latest_names_revids(self, index, query=None):
+    def _find_latest_backends_revids(self, index, query=None):
         """
-        find the latest revids using the all-revs index
+        find the latest revision identifiers using the all-revs index
 
         :param index: an up-to-date and open ALL_REVS index
         :param query: query to search only specific revisions (optional, default: all items/revisions)
-        :returns: a list of tuples (name, latest revid)
+        :returns: a list of tuples (backend name, latest revid)
         """
         if query is None:
             query = Every()
@@ -505,10 +544,10 @@
             result = searcher.search(query, groupedby=ITEMID, sortedby=FieldFacet(MTIME, reverse=True))
             by_item = result.groups(ITEMID)
             # values in v list are in same relative order as in results, so latest MTIME is first:
-            latest_names_revids = [(searcher.stored_fields(v[0])[NAME],
-                                    searcher.stored_fields(v[0])[REVID])
-                                   for v in by_item.values()]
-        return latest_names_revids
+            latest_backends_revids = [(searcher.stored_fields(v[0])[BACKENDNAME],
+                                      searcher.stored_fields(v[0])[REVID])
+                                      for v in by_item.values()]
+        return latest_backends_revids
 
     def rebuild(self, tmp=False, procs=1, limitmb=256):
         """
@@ -525,13 +564,13 @@
             # build an index of all we have (so we know what we have)
             all_revids = self.backend # the backend is an iterator over all revids
             self._modify_index(index, self.schemas[ALL_REVS], self.wikiname, all_revids, 'add', procs, limitmb)
-            latest_names_revids = self._find_latest_names_revids(index)
+            latest_backends_revids = self._find_latest_backends_revids(index)
         finally:
             index.close()
         # now build the index of the latest revisions:
         index = storage.open_index(LATEST_REVS)
         try:
-            self._modify_index(index, self.schemas[LATEST_REVS], self.wikiname, latest_names_revids, 'add', procs, limitmb)
+            self._modify_index(index, self.schemas[LATEST_REVS], self.wikiname, latest_backends_revids, 'add', procs, limitmb)
         finally:
             index.close()
 
@@ -551,24 +590,24 @@
         storage = self.get_storage(tmp)
         index_all = storage.open_index(ALL_REVS)
         try:
-            # NOTE: self.backend iterator gives (mountpoint, revid) tuples, which is NOT
+            # NOTE: self.backend iterator gives (backend_name, revid) tuples, which is NOT
             # the same as (name, revid), thus we do the set operations just on the revids.
             # first update ALL_REVS index:
-            revids_mountpoints = dict((revid, mountpoint) for mountpoint, revid in self.backend)
-            backend_revids = set(revids_mountpoints)
+            revids_backends = dict((revid, backend_name) for backend_name, revid in self.backend)
+            backend_revids = set(revids_backends)
             with index_all.searcher() as searcher:
-                ix_revids_names = dict((doc[REVID], doc[NAME]) for doc in searcher.all_stored_fields())
-            revids_mountpoints.update(ix_revids_names) # this is needed for stuff that was deleted from storage
-            ix_revids = set(ix_revids_names)
+                ix_revids_backends = dict((doc[REVID], doc[BACKENDNAME]) for doc in searcher.all_stored_fields())
+            revids_backends.update(ix_revids_backends) # this is needed for stuff that was deleted from storage
+            ix_revids = set(ix_revids_backends)
             add_revids = backend_revids - ix_revids
             del_revids = ix_revids - backend_revids
             changed = add_revids or del_revids
-            add_revids = [(revids_mountpoints[revid], revid) for revid in add_revids]
-            del_revids = [(revids_mountpoints[revid], revid) for revid in del_revids]
+            add_revids = [(revids_backends[revid], revid) for revid in add_revids]
+            del_revids = [(revids_backends[revid], revid) for revid in del_revids]
             self._modify_index(index_all, self.schemas[ALL_REVS], self.wikiname, add_revids, 'add')
             self._modify_index(index_all, self.schemas[ALL_REVS], self.wikiname, del_revids, 'delete')
 
-            backend_latest_names_revids = set(self._find_latest_names_revids(index_all))
+            backend_latest_backends_revids = set(self._find_latest_backends_revids(index_all))
         finally:
             index_all.close()
         index_latest = storage.open_index(LATEST_REVS)
@@ -576,9 +615,9 @@
             # now update LATEST_REVS index:
             with index_latest.searcher() as searcher:
                 ix_revids = set(doc[REVID] for doc in searcher.all_stored_fields())
-            backend_latest_revids = set(revid for name, revid in backend_latest_names_revids)
+            backend_latest_revids = set(revid for name, revid in backend_latest_backends_revids)
             upd_revids = backend_latest_revids - ix_revids
-            upd_revids = [(revids_mountpoints[revid], revid) for revid in upd_revids]
+            upd_revids = [(revids_backends[revid], revid) for revid in upd_revids]
             self._modify_index(index_latest, self.schemas[LATEST_REVS], self.wikiname, upd_revids, 'update')
             self._modify_index(index_latest, self.schemas[LATEST_REVS], self.wikiname, del_revids, 'delete')
         finally:
@@ -773,9 +812,20 @@
         """
         self.indexer = indexer
         self.backend = self.indexer.backend
+        self._name = query.get('name_exact')
         if latest_doc is None:
             # we need to call the method without acl check to avoid endless recursion:
-            latest_doc = self.indexer._document(**query) or {}
+            latest_doc = self.indexer._document(**query)
+            if latest_doc is None:
+                # no such item, create a dummy doc that has a NAME entry to
+                # avoid issues in the name(s) property code. if this was a
+                # lookup for some specific item (using a name_exact query), we
+                # put that name into the NAME list, otherwise it'll be empty:
+                if self._name is not None:
+                    names = [self._name, ]
+                else:
+                    names = []
+                latest_doc = {NAME: names}
         self._current = latest_doc
 
     def _get_itemid(self):
@@ -789,12 +839,50 @@
         return self._current.get(ACL)
 
     @property
+    def namespace(self):
+        return self._current.get(NAMESPACE)
+
+    @property
     def ptime(self):
         dt = self._current.get(PTIME)
         if dt is not None:
             return utctimestamp(dt)
 
     @property
+    def names(self):
+        return get_names(self._current)
+
+    @property
+    def parentnames(self):
+        """
+        compute list of parent names (same order as in names, but no dupes)
+
+        :return: parent names (list of unicode)
+        """
+        parent_names = []
+        for name in self.names:
+            parentname_tail = name.rsplit('/', 1)
+            if len(parentname_tail) == 2:
+                parent_name = parentname_tail[0]
+                if parent_name not in parent_names:
+                    parent_names.append(parent_name)
+        return parent_names
+
+    @property
+    def parentids(self):
+        """
+        compute list of parent itemids
+
+        :return: parent itemids (set)
+        """
+        parent_ids = set()
+        for parent_name in self.parentnames:
+            rev = self.indexer._document(idx_name=LATEST_REVS, name_exact=parent_name)
+            if rev:
+                parent_ids.add(rev[ITEMID])
+        return parent_ids
+
+    @property
     def mtime(self):
         dt = self._current.get(MTIME)
         if dt is not None:
@@ -802,7 +890,50 @@
 
     @property
     def name(self):
-        return self._current.get(NAME, 'DoesNotExist')
+        if self._name and self._name in self.names:
+            name = self._name
+        else:
+            try:
+                name = self.names[0]
+            except IndexError:
+                # empty name list, no name:
+                name = None
+        assert name is None or isinstance(name, unicode)
+        return name
+
+    def _fqname(self, name):
+        """
+        return the fully qualified name including the namespace: NS:NAME
+        """
+        ns = self.namespace
+        name = name or u''
+        if ns:
+            fqn = ns + u':' + name
+        else:
+            fqn = name
+        assert isinstance(fqn, unicode)
+        return fqn
+
+    @property
+    def fqname(self):
+        """
+        return the fully qualified name including the namespace: NS:NAME
+        """
+        return self._fqname(self.name)
+
+    @property
+    def fqnames(self):
+        """
+        return the fully qualified names including the namespace: NS:NAME
+        """
+        return [self._fqname(name) for name in self.names]
+
+    @property
+    def fqparentnames(self):
+        """
+        return the fully qualified parent names including the namespace: NS:NAME
+        """
+        return [self._fqname(name) for name in self.parentnames]
 
     @classmethod
     def create(cls, indexer, **query):
@@ -855,7 +986,7 @@
         """
         preprocess a revision before it gets stored and put into index.
         """
-        content = convert_to_indexable(meta, data, is_new=True)
+        content = convert_to_indexable(meta, data, self.name, is_new=True)
         return meta, data, content
 
     def store_revision(self, meta, data, overwrite=False,
@@ -898,11 +1029,12 @@
         if wikiname is None:
             wikiname = app.cfg.interwikiname
         state = {'trusted': trusted,
-                 keys.NAME: name,
+                 keys.NAME: [name],
                  keys.ACTION: action,
                  keys.ADDRESS: remote_addr,
                  keys.USERID: userid,
                  keys.WIKINAME: wikiname,
+                 keys.NAMESPACE: None,
                  keys.ITEMID: self.itemid, # real itemid or None
                  'contenttype_current': contenttype_current,
                  'contenttype_guessed': contenttype_guessed,
@@ -927,7 +1059,7 @@
         # just update the meta dict with the validated stuff:
         meta.update(dict(m.value.items()))
         # we do not want None / empty values:
-        meta = dict([(k, v) for k, v in meta.items() if v not in [None, []]])
+        meta = dict([(k, v) for k, v in meta.items() if v not in [None, ]]) # XXX do not kick out empty lists before fixing NAME processing
 
         if self.itemid is None:
             self.itemid = meta[ITEMID]
@@ -938,9 +1070,9 @@
                 raise ValueError('need overwrite=True to overwrite existing revisions')
         meta, data, content = self.preprocess(meta, data)
         data.seek(0)  # rewind file
-        revid = backend.store(meta, data)
+        backend_name, revid = backend.store(meta, data)
         meta[REVID] = revid
-        self.indexer.index_revision(meta, content)
+        self.indexer.index_revision(meta, content, backend_name)
         if not overwrite:
             self._current = self.indexer._document(revid=revid)
         if return_rev:
@@ -959,7 +1091,7 @@
         Destroy revision <revid>.
         """
         rev = Revision(self, revid)
-        self.backend.remove(rev.name, revid)
+        self.backend.remove(rev.backend_name, revid)
         self.indexer.remove_revision(revid)
 
     def destroy_all_revisions(self):
@@ -974,7 +1106,7 @@
     """
     An existing revision (exists in the backend).
     """
-    def __init__(self, item, revid, doc=None):
+    def __init__(self, item, revid, doc=None, name=None):
         is_current = revid == CURRENT
         if doc is None:
             if is_current:
@@ -990,18 +1122,40 @@
         self.item = item
         self.revid = revid
         self.backend = item.backend
+        self.backend_name = doc[BACKENDNAME]
         self._doc = doc
         self.meta = Meta(self, self._doc)
         self._data = None
+        if name and name in self.names:
+            self._name = name
+        else:
+            self._name = None
         # Note: this does not immediately raise a KeyError for non-existing revs any more
         # If you access data or meta, it will, though.
 
     @property
+    def names(self):
+        return get_names(self.meta)
+
+    @property
     def name(self):
-        return self.meta.get(NAME, 'DoesNotExist')
+        name = self._name
+        if name is None:
+            try:
+                name = self.names[0]
+            except IndexError:
+                # empty name list, no name:
+                name = None
+        return name
+
+    def set_context(self, context):
+        for name in self.names:
+            if name.startswith(context):
+                self._name = name
+                return
 
     def _load(self):
-        meta, data = self.backend.retrieve(self._doc[NAME], self.revid) # raises KeyError if rev does not exist
+        meta, data = self.backend.retrieve(self.backend_name, self.revid) # raises KeyError if rev does not exist
         self.meta = Meta(self, self._doc, meta)
         self._data = data
         return meta, data
--- a/MoinMoin/storage/middleware/protecting.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/protecting.py	Sun Feb 10 19:55:14 2013 +0100
@@ -19,11 +19,18 @@
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
+from whoosh.util import lru_cache
+
 from MoinMoin.config import ACL, CREATE, READ, PUBREAD, WRITE, DESTROY, ADMIN, \
                             PTIME, ACL_RIGHTS_CONTENTS, \
                             ALL_REVS, LATEST_REVS
 from MoinMoin.security import AccessControlList
 
+# max sizes of some lru caches:
+LOOKUP_CACHE = 100  # ACL lookup for some itemname
+PARSE_CACHE = 100  # ACL string -> ACL object parsing
+EVAL_CACHE = 500  # ACL evaluation for some username / capability
+
 
 class AccessDenied(Exception):
     """
@@ -56,14 +63,78 @@
         self.user = user
         self.acl_mapping = acl_mapping
         self.valid_rights = ACL_RIGHTS_CONTENTS
+        # The ProtectingMiddleware exists just 1 request long, but might have
+        # to parse and evaluate huge amounts of ACLs. We avoid doing same stuff
+        # again and again by using some fresh lru caches for each PMW instance.
+        lru_cache_decorator = lru_cache(PARSE_CACHE)
+        self.parse_acl = lru_cache_decorator(self._parse_acl)
+        lru_cache_decorator = lru_cache(EVAL_CACHE)
+        self.eval_acl = lru_cache_decorator(self._eval_acl)
+        lru_cache_decorator = lru_cache(LOOKUP_CACHE)
+        self.get_acls = lru_cache_decorator(self._get_acls)
 
-    def get_acls(self, itemname):
+    def _clear_acl_cache(self):
+        # if we have modified the backend somehow so ACL lookup is influenced,
+        # this functions need to get called, so it clears the ACL cache.
+        # ACL lookups afterwards will fetch fresh info from the lower layers.
+        self.get_acls.cache_clear()
+
+    def _get_configured_acls(self, itemname):
+        """
+        for a fully-qualified itemname (namespace:name), get the acl configuration
+        for that (part of the) namespace.
+
+        @param itemname: fully qualified itemname
+        @returns: acl configuration (acl dict from the acl_mapping)
+        """
         for prefix, acls in self.acl_mapping:
             if itemname.startswith(prefix):
                 return acls
         else:
             raise ValueError('No acl_mapping entry found for item {0!r}'.format(itemname))
 
+    def _get_acls(self, itemid=None, fqname=None):
+        """
+        return a list of (alternatively valid) effective acls for the item
+        identified via itemid or fqname.
+        this can be a list just containing the item's own acl (as only alternative),
+        or a list with None, indicating no acl was found (in non-hierarchic mode).
+        if hierarchic acl mode is enabled, a list of all valid parent acls will
+        be returned.
+        All lists are without considering before/default/after acls.
+        """
+
+        if itemid is not None:
+            item = self.get_item(itemid=itemid)
+        elif fqname is not None:
+            # itemid might be None for new, not yet stored items,
+            # but we have fqname then
+            item = self.get_item(name_exact=fqname)
+        else:
+            raise ValueError("need itemid or fqname")
+        acl = item.acl
+        fqname = item.fqname
+        if acl is not None:
+            return [acl, ]
+        acl_cfg = self._get_configured_acls(fqname)
+        if acl_cfg['hierarchic']:
+            # check parent(s), recursively
+            parentids = item.parentids
+            if parentids:
+                acl_list = []
+                for parentid in parentids:
+                    pacls = self.get_acls(parentid, None)
+                    acl_list.extend(pacls)
+                return acl_list
+        return [None, ]
+
+    def _parse_acl(self, acl, default=''):
+        return AccessControlList([acl, ], default=default, valid=self.valid_rights)
+
+    def _eval_acl(self, acl, default_acl, user_name, right):
+        aclobj = self.parse_acl(acl, default_acl)
+        return aclobj.may(user_name, right)
+
     def query_parser(self, default_fields, idx_name=LATEST_REVS):
         return self.indexer.query_parser(default_fields, idx_name=idx_name)
 
@@ -111,9 +182,17 @@
         item = self.indexer.existing_item(**query)
         return ProtectedItem(self, item)
 
-    def may(self, itemname, capability, username=None):
+    def may(self, itemname, capability, usernames=None):
+        if usernames is not None and isinstance(usernames, (str, unicode)):
+            # we got a single username (maybe str), make a list of unicode:
+            if isinstance(usernames, str):
+                usernames = usernames.decode('utf-8')
+            usernames = [usernames, ]
+        if isinstance(itemname, list):
+            # if we get a list of names, just use first one to fetch item
+            itemname = itemname[0]
         item = self[itemname]
-        allowed = item.allows(capability, user_name=username)
+        allowed = item.allows(capability, user_names=usernames)
         return allowed
 
 
@@ -131,85 +210,63 @@
         return self.item.itemid
 
     @property
+    def fqname(self):
+        return self.item.fqname
+
+    @property
+    def parentids(self):
+        return self.item.parentids
+
+    @property
+    def parentnames(self):
+        return self.item.parentnames
+
+    @property
     def name(self):
         return self.item.name
 
+    @property
+    def acl(self):
+        return self.item.acl
+
     def __nonzero__(self):
         return bool(self.item)
 
-    def _allows(self, right, user_name):
-        """
-        check permissions in this item without considering before/after acls
+    def full_acls(self):
         """
-        acls = self.protector.get_acls(self.item.name)
-        acl = self.item.acl
-        if acl is not None:
-            # If the item has an acl (even one that doesn't match) we *do not*
-            # check the parents. We only check the parents if there's no acl on
-            # the item at all.
-            acl = AccessControlList([acl, ], acls['default'], valid=self.protector.valid_rights)
-            allowed = acl.may(user_name, right)
-            if allowed is not None:
-                return pchecker(right, allowed, self.item)
-        else:
-            if acls['hierarchic']:
-                # check parent(s), recursively
-                parent_tail = self.item.name.rsplit('/', 1)
-                if len(parent_tail) == 2:
-                    parent, _ = parent_tail
-                    parent_item = self.protector[parent]
-                    allowed = parent_item._allows(right, user_name)
-                    if allowed is not None:
-                        return pchecker(right, allowed, self.item)
+        iterator over all alternatively possible full acls for this item,
+        including before/default/after acl.
+        """
+        fqname = self.item.fqname
+        itemid = self.item.itemid
+        acl_cfg = self.protector._get_configured_acls(fqname)
+        before_acl = acl_cfg['before']
+        after_acl = acl_cfg['after']
+        for item_acl in self.protector.get_acls(itemid, fqname):
+            if item_acl is None:
+                item_acl = acl_cfg['default']
+            yield u' '.join([before_acl, item_acl, after_acl])
 
-            acl = AccessControlList([acls['default'], ], valid=self.protector.valid_rights)
-            allowed = acl.may(user_name, right)
-            if allowed is not None:
-                return pchecker(right, allowed, self.item)
-
-    def allows(self, right, user_name=None):
-        """ Check if username may have <right> access on item <itemname>.
-
-        For hierarchic=False we just check the item in question.
+    def allows(self, right, user_names=None):
+        """ Check if usernames may have <right> access on this item.
 
-        For hierarchic=True, we check each item in the hierarchy. We
-        start with the deepest item and recurse to the top of the tree.
-        If one of those permits, True is returned.
-        This is done *only* if there is *no ACL at all* (not even an empty one)
-        on the items we 'recurse over'.
-
-        For both configurations, we check `before` before the item/default
-        acl and `after` after the item/default acl, of course.
-
-        `default` is only used if there is no ACL on the item (and none on
-        any of the item's parents when using hierarchic.)
-
-        :param itemname: item to get permissions from
         :param right: the right to check
-        :param username: username to use for permissions check (default is to
-                         use the username doing the current request)
+        :param user_names: user names to use for permissions check (default is to
+                          use the user names doing the current request)
         :rtype: bool
         :returns: True if you have permission or False
         """
-        if user_name is None:
-            user_name = self.protector.user.name
-
-        acls = self.protector.get_acls(self.item.name)
-
-        before = AccessControlList([acls['before'], ], valid=self.protector.valid_rights)
-        allowed = before.may(user_name, right)
-        if allowed is not None:
-            return pchecker(right, allowed, self.item)
-
-        allowed = self._allows(right, user_name)
-        if allowed is not None:
-            return pchecker(right, allowed, self.item)
-
-        after = AccessControlList([acls['after'], ], valid=self.protector.valid_rights)
-        allowed = after.may(user_name, right)
-        if allowed is not None:
-            return pchecker(right, allowed, self.item)
-
+        if user_names is None:
+            user_names = self.protector.user.name
+        # must be a non-empty list of user names
+        assert isinstance(user_names, list)
+        assert user_names
+        acl_cfg = self.protector._get_configured_acls(self.item.fqname)
+        for user_name in user_names:
+            for full_acl in self.full_acls():
+                allowed = self.protector.eval_acl(full_acl, acl_cfg['default'], user_name, right)
+                if allowed is True and pchecker(right, allowed, self.item):
+                    return True
         return False
 
     def require(self, *capabilities):
@@ -240,16 +297,19 @@
         if overwrite:
             self.require(DESTROY)
         rev = self.item.store_revision(meta, data, overwrite=overwrite, return_rev=return_rev, **kw)
+        self.protector._clear_acl_cache()
         if return_rev:
             return ProtectedRevision(self.protector, rev, p_item=self)
 
     def store_all_revisions(self, meta, data):
         self.require(DESTROY)
         self.item.store_all_revisions(meta, data)
+        self.protector._clear_acl_cache()
 
     def destroy_revision(self, revid):
         self.require(DESTROY)
         self.item.destroy_revision(revid)
+        self.protector._clear_acl_cache()
 
     def destroy_all_revisions(self):
         for rev in self.item.iter_revs():
@@ -283,6 +343,10 @@
         return self.rev.revid
 
     @property
+    def name(self):
+        return self.rev.name
+
+    @property
     def meta(self):
         self.require(READ, PUBREAD)
         return self.rev.meta
@@ -292,6 +356,9 @@
         self.require(READ, PUBREAD)
         return self.rev.data
 
+    def set_context(self, context):
+        self.rev.set_context(context)
+
     def close(self):
         self.rev.close()
 
--- a/MoinMoin/storage/middleware/routing.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/routing.py	Sun Feb 10 19:55:14 2013 +0100
@@ -1,116 +1,136 @@
-# Copyright: 2008-2011 MoinMoin:ThomasWaldmann
+# Copyright: 2011 MoinMoin:ThomasWaldmann
 # Copyright: 2011 MoinMoin:RonnyPfannschmidt
-# Copyright: 2009 MoinMoin:ChristopherDenter
 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
 
 """
-MoinMoin - routing middleware
-
-Routes requests to different backends depending on the item name.
+MoinMoin - namespaces middleware
 
-Just think of UNIX filesystems, fstab and mount.
-
-This middleware lets you mount backends that store items belonging to some
-specific part of the namespace. Routing middleware has same API as a backend.
+Routes requests to different backends depending on the namespace.
 """
 
 
 from __future__ import absolute_import, division
 
-from MoinMoin.config import NAME
+from MoinMoin.config import NAME, BACKENDNAME, NAMESPACE
 
 from MoinMoin.storage.backends import BackendBase, MutableBackendBase
 
 
 class Backend(MutableBackendBase):
     """
-    router, behaves readonly for readonly mounts
+    namespace dispatcher, behaves readonly for readonly mounts
     """
-    def __init__(self, mapping):
+    def __init__(self, namespaces, backends):
         """
-        Initialize router backend.
+        Initialize.
 
-        The mapping given must satisfy the following criteria:
+        The namespace mapping given must satisfy the following criteria:
             * Order matters.
-            * Mountpoints are just item names, including the special '' (empty)
-              root item name.
-            * Trailing '/' of a mountpoint will be stripped.
-            * There *must* be a backend with mountpoint '' at the very
-              end of the mapping. That backend is then used as root, which means
-              that all items that don't lie in the namespace of any other
-              backend are stored there.
+            * Namespaces are unicode strings like u'' (default ns), u'userprofiles:'
+              (used to store userprofiles) or u'files:' (could map to a fileserver
+              backend). Can be also a hierarchic ns spec like u'foo:bar:'.
+            * There *must* be a default namespace entry for u'' at the end of
+              the list.
 
-        :type mapping: list of tuples of mountpoint -> backend mappings
-        :param mapping: [(mountpoint, backend), ...]
+        namespaces = [
+            (u'userprofiles:', 'user_be'),
+            (u'', 'default_be'), # default (u'') must be last
+        ]
+
+        The backends mapping maps backend names to backend instances:
+
+        backends = {
+            'default_be': BackendInstance1,
+            'user_be': BackendInstance2,
+        }
+
+        :type namespaces: list of tuples of namespace specifier -> backend names
+        :param mapping: [(namespace, backend_name), ...]
+        :type backends: dict backend names -> backends
+        :param backends: {backend_name: backend, ...}
         """
-        self.mapping = [(mountpoint.rstrip('/'), backend) for mountpoint, backend in mapping]
+        self.namespaces = namespaces
+        self.backends = backends
 
     def open(self):
-        for mountpoint, backend in self.mapping:
+        for backend in self.backends.values():
             backend.open()
 
     def close(self):
-        for mountpoint, backend in self.mapping:
+        for backend in self.backends.values():
             backend.close()
 
-    def _get_backend(self, itemname):
+    def _get_backend(self, fq_names):
         """
-        For a given fully-qualified itemname (i.e. something like Company/Bosses/Mr_Joe)
-        find the backend it belongs to (given by this instance's mapping), the local
-        itemname inside that backend and the mountpoint of the backend.
+        For a given fully-qualified itemname (i.e. something like ns:itemname)
+        find the backend it belongs to, the itemname without namespace
+        spec and the namespace of the backend.
 
-        :param itemname: fully-qualified itemname
-        :returns: tuple of (backend, local itemname, mountpoint)
+        :param fq_name: fully-qualified itemnames
+        :returns: tuple of (backend name, local item name, namespace)
         """
-        for mountpoint, backend in self.mapping:
-            if itemname == mountpoint or itemname.startswith(mountpoint and mountpoint + '/' or ''):
-                lstrip = mountpoint and len(mountpoint)+1 or 0
-                return backend, itemname[lstrip:], mountpoint
-        raise AssertionError("No backend found for {0!r}. Available backends: {1!r}".format(itemname, self.mapping))
+        fq_name = fq_names[0]
+        for namespace, backend_name in self.namespaces:
+            if fq_name.startswith(namespace):
+                item_names = [fq_name[len(namespace):] for fq_name in fq_names]
+                return backend_name, item_names, namespace.rstrip(':')
+        raise AssertionError("No backend found for {0!r}. Namespaces: {1!r}".format(itemname, self.namespaces))
 
     def __iter__(self):
-        # Note: yields <backend_mountpoint>/<backend_revid> as router revid, so that this
-        #       can be given to get_revision and be routed to the right backend.
-        for mountpoint, backend in self.mapping:
-            for revid in backend:
-                yield (mountpoint, revid)
+        # Note: yields enough information so we can retrieve the revision from
+        #       the right backend later (this is more than just the revid).
+        for backend_name, backend in self.backends.items():
+            for revid in backend: # TODO maybe directly yield the backend?
+                yield (backend_name, revid)
 
-    def retrieve(self, name, revid):
-        backend, _, mountpoint = self._get_backend(name)
+    def retrieve(self, backend_name, revid):
+        backend = self.backends[backend_name]
         meta, data = backend.retrieve(revid)
-        if mountpoint:
-            name = meta[NAME]
-            if name:
-                meta[NAME] = u'{0}/{1}'.format(mountpoint, meta[NAME])
-            else:
-                meta[NAME] = mountpoint # no trailing slash!
         return meta, data
 
     # writing part
     def create(self):
-        for mountpoint, backend in self.mapping:
+        for backend in self.backends.values():
             if isinstance(backend, MutableBackendBase):
                 backend.create()
-            #XXX else: log info?
 
     def destroy(self):
-        for mountpoint, backend in self.mapping:
+        for backend in self.backends.values():
             if isinstance(backend, MutableBackendBase):
                 backend.destroy()
-            #XXX else: log info?
 
     def store(self, meta, data):
-        mountpoint_itemname = meta[NAME]
-        backend, itemname, mountpoint = self._get_backend(mountpoint_itemname)
+        namespace = meta.get(NAMESPACE)
+        if namespace is None:
+            # if there is no NAMESPACE in metadata, we assume that the NAME
+            # is fully qualified and determine the namespace from it:
+            fq_names = meta[NAME]
+            assert isinstance(fq_names, list)
+            if fq_names:
+                backend_name, item_names, namespace = self._get_backend(fq_names)
+                # side effect: update the metadata with namespace and short item name (no ns)
+                meta[NAMESPACE] = namespace
+                meta[NAME] = item_names
+            else:
+                raise ValueError('can not determine namespace: empty NAME list, no NAMESPACE metadata present')
+        else:
+            if namespace:
+                namespace += u':' # needed for _get_backend
+            backend_name, _, _ = self._get_backend([namespace])
+        backend = self.backends[backend_name]
+
         if not isinstance(backend, MutableBackendBase):
-            raise TypeError('backend {0!r} mounted at {1!r} is readonly'.format(backend, mountpoint))
-        meta[NAME] = itemname
+            raise TypeError('backend {0} is readonly!'.format(backend_name))
+
         revid = backend.store(meta, data)
-        meta[NAME] = mountpoint_itemname # restore the original name
-        return revid
 
-    def remove(self, name, revid):
-        backend, _, mountpoint = self._get_backend(name)
+        # add the BACKENDNAME after storing, so it gets only into
+        # the index, but not in stored metadata:
+        meta[BACKENDNAME] = backend_name
+        return backend_name, revid
+
+    def remove(self, backend_name, revid):
+        backend = self.backends[backend_name]
         if not isinstance(backend, MutableBackendBase):
-            raise TypeError('backend {0!r} mounted at {1!r} is readonly'.format(backend, mountpoint))
+            raise TypeError('backend {0} is readonly'.format(backend_name))
         backend.remove(revid)
--- a/MoinMoin/storage/middleware/serialization.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/serialization.py	Sun Feb 10 19:55:14 2013 +0100
@@ -45,8 +45,13 @@
             yield block
 
 def serialize_iter(backend):
-    for mountpoint, revid in backend:
-        meta, data = backend.retrieve(mountpoint, revid)
+    for revid in backend:
+        if isinstance(revid, tuple):
+            # router middleware gives tuples and wants both values for retrieve:
+            meta, data = backend.retrieve(*revid)
+        else:
+            # lower level backends have simple revids
+            meta, data = backend.retrieve(revid)
         for data in serialize_rev(meta, data):
             yield data
     for data in serialize_rev(None, None):
@@ -61,6 +66,13 @@
         meta_str = src.read(meta_size)
         text = meta_str.decode('utf-8')
         meta = json.loads(text)
+        name = meta.get('name')
+        if isinstance(name, unicode):
+            # if we encounter single names, make a list of names:
+            meta['name'] = [name, ]
+        if 'itemtype' not in meta:
+            # temporary hack to upgrade serialized item files:
+            meta['itemtype'] = u'default'
         data_size = meta[u'size']
         curr_pos = src.tell()
         limited = LimitedStream(src, data_size)
--- a/MoinMoin/storage/middleware/validation.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/storage/middleware/validation.py	Sun Feb 10 19:55:14 2013 +0100
@@ -132,6 +132,15 @@
     return name_validator(element, state)
 
 
+def namespace_validator(element, state):
+    """
+    a namespace (part of a wiki site)
+    """
+    if element.raw is Unset:
+        element.set(state[keys.NAMESPACE])
+    return name_validator(element, state)
+
+
 def user_contenttype_validator(element, state):
     """
     user profile content type
@@ -309,8 +318,9 @@
     String.named(keys.REVID).validated_by(revid_validator),
     String.named(keys.PARENTID).validated_by(uuid_validator).using(optional=True),
     String.named(keys.WIKINAME).using(strip=False).validated_by(wikiname_validator),
-    String.named(keys.NAME).using(strip=False).validated_by(name_validator),
-    String.named(keys.NAME_OLD).using(strip=False).validated_by(name_validator).using(optional=True),
+    String.named(keys.NAMESPACE).using(strip=False).validated_by(namespace_validator),
+    List.named(keys.NAME).of(String.using(strip=False).validated_by(name_validator)),
+    List.named(keys.NAME_OLD).of(String.using(strip=False).validated_by(name_validator)).using(optional=True),
     Integer.named(keys.MTIME).validated_by(mtime_validator),
     String.named(keys.ACTION).validated_by(action_validator),
     String.named(keys.ACL).validated_by(acl_validator),
--- a/MoinMoin/templates/global_history.html	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/templates/global_history.html	Sun Feb 10 19:55:14 2013 +0100
@@ -28,13 +28,13 @@
                                 <span class="moin-history-{{ meta.action|lower }}"></span>
                             </td>
                             <td class="moin-history-links">
-                                <a href="{{ url_for('frontend.history', item_name=meta.name, bookmark=bookmark_time) }}">HIST</a>
+                                <a href="{{ url_for('frontend.history', item_name=meta.name[0], bookmark=bookmark_time) }}">HIST</a>
                                 {% if bookmark_time -%}
-                                    <a href="{{ url_for('frontend.diff', item_name=meta.name, bookmark=bookmark_time) }}">DIFF</a>
+                                    <a href="{{ url_for('frontend.diff', item_name=meta.name[0], bookmark=bookmark_time) }}">DIFF</a>
                                 {%- endif %}
                             </td>
                             <td class="moin-history-time">{{ meta.mtime|timeformat }}</td>
-                            <td class="moin-history-item"><a class="{{ meta.contenttype|contenttype_to_class }}" href="{{ url_for('frontend.show_item', item_name=meta.name) }}" title="{{ meta.contenttype }}">{{ meta.name }}</a></td>
+                            <td class="moin-history-item"><a class="{{ meta.contenttype|contenttype_to_class }}" href="{{ url_for('frontend.show_item', item_name=meta.name[0]) }}" title="{{ meta.contenttype }}">{{ meta.name|join(' | ') }}</a></td>
                         </tr>
                     {% endfor %}
                     </table>
--- a/MoinMoin/templates/history.html	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/templates/history.html	Sun Feb 10 19:55:14 2013 +0100
@@ -35,7 +35,7 @@
             <tbody>
                 {% for doc in history %}
                 <tr>
-                    <td class="moin-wordbreak">{{ doc.name }}</td>
+                    <td class="moin-wordbreak">{{ doc.name|join(' | ') }}</td>
                     <td>{{ doc.revid | shorten_id }}</td>
                     <td>{{ doc.mtime|datetimeformat }}</td>
                     <td class="moin-integer">{{ doc.size }}</td>
@@ -48,15 +48,15 @@
                     <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, rev=doc.revid) }}">{{ _('show') }}</a></td>
-                    <td><a href="{{ url_for('frontend.show_item_meta', item_name=doc.name, rev=doc.revid) }}">{{ _('meta') }}</a></td>
-                    <td><a href="{{ url_for('frontend.download_item', item_name=doc.name, rev=doc.revid) }}">{{ _('download') }}</a></td>
-                    <td><a href="{{ url_for('frontend.highlight_item', item_name=doc.name, rev=doc.revid) }}">{{ _('highlight') }}</a></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, rev=doc.revid) }}">{{ _('revert') }}</a></td>
+                    <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, rev=doc.revid) }}">{{ _('destroy') }}</a></td>
+                    <td><a href="{{ url_for('frontend.destroy_item', item_name=doc.name[0], rev=doc.revid) }}">{{ _('destroy') }}</a></td>
                     {%- endif %}
                 </tr>
                 {% endfor %}
--- a/MoinMoin/templates/layout.html	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/templates/layout.html	Sun Feb 10 19:55:14 2013 +0100
@@ -42,9 +42,9 @@
                 <img id="moin-avatar" src="{{ avatar }}" />
             {%- endif %}
             {% if user.name -%}
-                {% set wiki_href, aliasname, title, exists = theme_supp.userhome() %}
+                {% set wiki_href, display_name, title, exists = theme_supp.userhome() %}
                 <a href="{{ wiki_href }}" {% if not exists %}class="moin-nonexistent"{% endif %} rel="nofollow" title="{{ title }}">
-                    {{ aliasname }}
+                    {{ display_name }}
                 </a>
                 {% if 'frontend.usersettings' not in cfg.endpoints_excluded -%}
                     <span class="sep"> | </span>
--- a/MoinMoin/templates/search.html	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/templates/search.html	Sun Feb 10 19:55:14 2013 +0100
@@ -44,7 +44,7 @@
                         {% if result['wikiname'] == cfg.interwikiname %}
                             <tr>
                                 <td class="moin-wordbreak">{{ result.pos + 1 }}
-                                <a href="{{ url_for_item(item_name=result['name'], wiki_name='Self', rev=result['revid']) }}"><b>{{ result['name'] }}</b></a>
+                                <a href="{{ url_for_item(item_name=result['name'][0], wiki_name='Self', rev=result['revid']) }}"><b>{{ result['name'] | join(' | ')}}</b></a>
                                 </td>
                             </tr>
                             {% if result['summary'] %}
--- a/MoinMoin/templates/usersettings_forms.html	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/templates/usersettings_forms.html	Sun Feb 10 19:55:14 2013 +0100
@@ -5,7 +5,7 @@
 {{ forms.render_errors(form) }}
 <dl>
     {{ forms.render(form['name']) }}
-    {{ forms.render(form['aliasname']) }}
+    {{ forms.render(form['display_name']) }}
     {{ forms.render(form['openid']) }}
     {{ forms.render(form['timezone']) }}
     {{ forms.render(form['locale']) }}
--- a/MoinMoin/themes/__init__.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/themes/__init__.py	Sun Feb 10 19:55:14 2013 +0100
@@ -105,9 +105,9 @@
         breadcrumbs = []
         trail = user.get_trail()
         for interwiki_item_name in trail:
-            wiki_name, item_name = split_interwiki(interwiki_item_name)
+            wiki_name, namespace, item_name = split_interwiki(interwiki_item_name)
             err = not is_known_wiki(wiki_name)
-            href = url_for_item(item_name, wiki_name=wiki_name)
+            href = url_for_item(item_name, namespace=namespace, wiki_name=wiki_name)
             if is_local_wiki(wiki_name):
                 exists = self.storage.has_item(item_name)
                 wiki_name = ''  # means "this wiki" for the theme code
@@ -132,16 +132,13 @@
         Assemble arguments used to build user homepage link
 
         :rtype: tuple
-        :returns: arguments of user homepage link in tuple (wiki_href, aliasname, title, exists)
+        :returns: arguments of user homepage link in tuple (wiki_href, display_name, title, exists)
         """
         user = self.user
-        name = user.name
-        aliasname = user.aliasname
-        if not aliasname:
-            aliasname = name
-
+        name = user.name0
+        display_name = user.display_name or name
         wikiname, itemname = getInterwikiHome(name)
-        title = u"{0} @ {1}".format(aliasname, wikiname)
+        title = u"{0} @ {1}".format(display_name, wikiname)
         # link to (interwiki) user homepage
         if is_local_wiki(wikiname):
             exists = self.storage.has_item(itemname)
@@ -149,7 +146,7 @@
             # We cannot check if wiki pages exists in remote wikis
             exists = True
         wiki_href = url_for_item(itemname, wiki_name=wikiname)
-        return wiki_href, aliasname, title, exists
+        return wiki_href, display_name, title, exists
 
     def split_navilink(self, text):
         """
@@ -194,10 +191,10 @@
         if target.startswith("wiki:"):
             target = target[5:]
 
-        wiki_name, item_name = split_interwiki(target)
+        wiki_name, namespace, item_name = split_interwiki(target)
         if wiki_name == 'Self':
             wiki_name = ''
-        href = url_for_item(item_name, wiki_name=wiki_name)
+        href = url_for_item(item_name, namespace=namespace, wiki_name=wiki_name)
         if not title:
             title = item_name
         return href, title, wiki_name
@@ -317,16 +314,14 @@
     userid = meta.get(USERID)
     if userid:
         u = user.User(userid)
-        name = u.name
+        name = u.name0
         text = name
-        aliasname = u.aliasname
-        if not aliasname:
-            aliasname = name
+        display_name = u.display_name or name
         if title:
             # we already have some address info
-            title = u"{0} @ {1}".format(aliasname, title)
+            title = u"{0} @ {1}".format(display_name, title)
         else:
-            title = aliasname
+            title = display_name
         if u.mailto_author and u.email:
             email = u.email
             css = 'editor mail'
@@ -435,7 +430,7 @@
                             'storage': flaskg.storage,
                             'clock': flaskg.clock,
                             'cfg': app.cfg,
-                            'item_name': 'handlers need to give it',
+                            'item_name': u'handlers need to give it',
                             'url_for_item': url_for_item,
                             'get_editor_info': lambda meta: get_editor_info(meta),
                             'utctimestamp': lambda dt: utctimestamp(dt),
--- a/MoinMoin/translations/MoinMoin.pot	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/translations/MoinMoin.pot	Sun Feb 10 19:55:14 2013 +0100
@@ -1,14 +1,14 @@
 # Translations template for moin.
-# Copyright (C) 2012 Moin Core Team, see http://moinmo.in/MoinCoreTeamGroup
+# Copyright (C) 2011 Moin Core Team, see http://moinmo.in/MoinCoreTeamGroup
 # This file is distributed under the same license as the moin project.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
 #
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: moin 2.0.0a0\n"
 "Report-Msgid-Bugs-To: English <moin-user@lists.sourceforge.net>\n"
-"POT-Creation-Date: 2012-08-26 22:24+0200\n"
+"POT-Creation-Date: 2011-10-30 23:56-0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,66 +17,6 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 0.9.6\n"
 
-#: MoinMoin/forms.py:45
-msgid "Invalid JSON."
-msgstr ""
-
-#: MoinMoin/forms.py:58
-msgid "OpenID"
-msgstr ""
-
-#: MoinMoin/forms.py:58
-msgid "OpenID address"
-msgstr ""
-
-#: MoinMoin/forms.py:60
-msgid "Your OpenID address"
-msgstr ""
-
-#: MoinMoin/forms.py:62
-msgid "E-Mail"
-msgstr ""
-
-#: MoinMoin/forms.py:62
-msgid "E-Mail address"
-msgstr ""
-
-#: MoinMoin/forms.py:64
-msgid "Your E-Mail address"
-msgstr ""
-
-#: MoinMoin/forms.py:66
-msgid "Password"
-msgstr ""
-
-#: MoinMoin/forms.py:91 MoinMoin/config/default.py:336
-msgid "Tags"
-msgstr ""
-
-#: MoinMoin/forms.py:93
-msgid "Search Query"
-msgstr ""
-
-#: MoinMoin/forms.py:139
-msgid "YYYY-MM-DD HH:MM:SS (example: 2013-12-31 23:59:59)"
-msgstr ""
-
-#: MoinMoin/forms.py:140
-msgid "Please use the following format: YYYY-MM-DD HH:MM:SS"
-msgstr ""
-
-#: MoinMoin/forms.py:144
-msgid "OK"
-msgstr ""
-
-#: MoinMoin/forms.py:160
-msgid "Invalid Reference."
-msgstr ""
-
-#: MoinMoin/forms.py:167
-msgid "(None)"
-msgstr ""
-
 #: MoinMoin/user.py:54
 #, python-format
 msgid ""
@@ -89,26 +29,26 @@
 msgid "This user name already belongs to somebody else."
 msgstr ""
 
-#: MoinMoin/user.py:68
+#: MoinMoin/user.py:66
 #, python-format
 msgid "Password not acceptable: %(msg)s"
 msgstr ""
 
-#: MoinMoin/user.py:78
+#: MoinMoin/user.py:81
 msgid ""
 "Please provide your email address. If you lose your login information, "
 "you can get it by email."
 msgstr ""
 
-#: MoinMoin/user.py:84
+#: MoinMoin/user.py:87
 msgid "This email already belongs to somebody else."
 msgstr ""
 
-#: MoinMoin/user.py:90
+#: MoinMoin/user.py:92
 msgid "This OpenID already belongs to somebody else."
 msgstr ""
 
-#: MoinMoin/user.py:730
+#: MoinMoin/user.py:699
 #, python-format
 msgid ""
 "Somebody has requested to email you a password recovery link.\n"
@@ -121,132 +61,145 @@
 "\n"
 msgstr ""
 
-#: MoinMoin/user.py:733
+#: MoinMoin/user.py:702
 #, python-format
 msgid "[%(sitename)s] Your wiki password recovery link"
 msgstr ""
 
-#: MoinMoin/user.py:751
-#, python-format
-msgid ""
-"Somebody has created an account with this email address.\n"
-"\n"
-"Please use the link below to verify your email address:\n"
-"\n"
-"%(link)s\n"
-"\n"
-"If you didn't create this account, please ignore this email.\n"
-"\n"
-msgstr ""
-
-#: MoinMoin/user.py:754
-#, python-format
-msgid "[%(sitename)s] Please verify your email address"
-msgstr ""
-
-#: MoinMoin/apps/admin/views.py:31 MoinMoin/config/default.py:338
+#: MoinMoin/apps/admin/views.py:30
 msgid "Admin"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:35 MoinMoin/config/default.py:337
-msgid "User"
-msgstr ""
-
-#: MoinMoin/apps/admin/views.py:53
-#: MoinMoin/apps/admin/templates/admin/index.html:5
+#: MoinMoin/apps/admin/views.py:48
+#: MoinMoin/apps/admin/templates/admin/index.html:9
 msgid "User Browser"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:65
+#: MoinMoin/apps/admin/views.py:60
 #, python-format
 msgid "User profile of %(username)s: %(email)r"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:109
+#: MoinMoin/apps/admin/views.py:104
 #: MoinMoin/apps/admin/templates/admin/sysitems_upgrade.html:3
 msgid "System items upgrade"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:115
+#: MoinMoin/apps/admin/views.py:110
 #, python-format
 msgid "System items upgrade failed due to the following error: %(error)s."
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:117
+#: MoinMoin/apps/admin/views.py:112
 msgid "System items have been upgraded successfully!"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:164
+#: MoinMoin/apps/admin/views.py:159
 msgid "Wiki Configuration"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:193
+#: MoinMoin/apps/admin/views.py:188
 msgid "Wiki Configuration Help"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:201
+#: MoinMoin/apps/admin/views.py:196
 msgid "Lexer description"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:202
+#: MoinMoin/apps/admin/views.py:197
 msgid "Lexer names"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:203
+#: MoinMoin/apps/admin/views.py:198
 msgid "File patterns"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:204
+#: MoinMoin/apps/admin/views.py:199
 msgid "Mimetypes"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:210
+#: MoinMoin/apps/admin/views.py:205
 msgid "Highlighter Help"
 msgstr ""
 
+#: MoinMoin/apps/admin/views.py:213
+msgid "InterWiki name"
+msgstr ""
+
+#: MoinMoin/apps/admin/views.py:214
+msgid "URL"
+msgstr ""
+
 #: MoinMoin/apps/admin/views.py:218
-msgid "InterWiki name"
-msgstr ""
-
-#: MoinMoin/apps/admin/views.py:219
-msgid "URL"
-msgstr ""
-
-#: MoinMoin/apps/admin/views.py:223
 msgid "Interwiki Help"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:231 MoinMoin/converter/archive_in.py:59
-#: MoinMoin/templates/atom.html:8 MoinMoin/templates/diff_text_atom.html:9
-#: MoinMoin/templates/history.html:27
+#: MoinMoin/apps/admin/views.py:226 MoinMoin/templates/history.html:23
 msgid "Size"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:232 MoinMoin/templates/index.html:153
+#: MoinMoin/apps/admin/views.py:227 MoinMoin/templates/index.html:153
 msgid "Item name"
 msgstr ""
 
-#: MoinMoin/apps/admin/views.py:238
+#: MoinMoin/apps/admin/views.py:233
 msgid "Item Size"
 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:6
+#: 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:7
+#: MoinMoin/apps/admin/templates/admin/index.html:11
 msgid "Show Wiki Configuration"
 msgstr ""
 
-#: MoinMoin/apps/admin/templates/admin/index.html:8
+#: 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:824 MoinMoin/apps/frontend/views.py:826
+msgid "Wanted Items"
+msgstr ""
+
+#: MoinMoin/apps/admin/templates/admin/index.html:17
+#: MoinMoin/apps/frontend/views.py:840 MoinMoin/apps/frontend/views.py:843
+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:5
 msgid ""
 "You can upgrade your system items by uploading an xml file with new items"
@@ -266,7 +219,7 @@
 msgstr ""
 
 #: MoinMoin/apps/admin/templates/admin/userbrowser.html:8
-#: MoinMoin/templates/history.html:32
+#: MoinMoin/templates/history.html:28
 msgid "Actions"
 msgstr ""
 
@@ -312,7 +265,6 @@
 msgstr ""
 
 #: MoinMoin/apps/admin/templates/admin/wikiconfighelp.html:13
-#: MoinMoin/constants/itemtypes.py:29
 msgid "Default"
 msgstr ""
 
@@ -320,402 +272,401 @@
 msgid "Description"
 msgstr ""
 
-#: MoinMoin/apps/admin/templates/user/highlighterhelp.html:4
-#: MoinMoin/apps/admin/templates/user/index_user.html:13
-msgid "Available Highlighters"
-msgstr ""
-
-#: MoinMoin/apps/admin/templates/user/index_user.html:3
-msgid "Documentation"
-msgstr ""
-
-#: MoinMoin/apps/admin/templates/user/index_user.html:5
-msgid "Documentation (local)"
-msgstr ""
-
-#: MoinMoin/apps/admin/templates/user/index_user.html:7
-msgid "User Menu"
-msgstr ""
-
-#: MoinMoin/apps/admin/templates/user/index_user.html:9
-#: MoinMoin/apps/frontend/views.py:928 MoinMoin/apps/frontend/views.py:930
-msgid "Wanted Items"
-msgstr ""
-
-#: MoinMoin/apps/admin/templates/user/index_user.html:10
-#: MoinMoin/apps/frontend/views.py:944 MoinMoin/apps/frontend/views.py:947
-msgid "Orphaned Items"
-msgstr ""
-
-#: MoinMoin/apps/admin/templates/user/index_user.html:11
-#: MoinMoin/apps/admin/templates/user/itemsize.html:4
-msgid "Item sizes (latest revision)"
-msgstr ""
-
-#: MoinMoin/apps/admin/templates/user/index_user.html:12
-#: MoinMoin/apps/admin/templates/user/interwikihelp.html:4
-msgid "Known InterWiki names"
-msgstr ""
-
-#: MoinMoin/apps/feed/views.py:85
+#: MoinMoin/apps/feed/views.py:81
 msgid "MoinMoin feels unhappy."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:139 MoinMoin/search/__init__.py:29
+#: MoinMoin/apps/frontend/views.py:130
+msgid "Search query too short."
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:141
+msgid "Search Query"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:142
 msgid "search also in non-current revisions"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:140 MoinMoin/apps/frontend/views.py:164
-#: MoinMoin/templates/lookup.html:6
-msgid "Lookup"
+#: MoinMoin/apps/frontend/views.py:143 MoinMoin/apps/frontend/views.py:150
+msgid "Search"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:503
+#: MoinMoin/apps/frontend/views.py:391 MoinMoin/templates/history.html:27
+msgid "Comment"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:391
+msgid "Comment about your change"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:392
+msgid "OK"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:395
 msgid "Target"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:503
+#: MoinMoin/apps/frontend/views.py:395
 msgid "The name of the target item"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:740
+#: MoinMoin/apps/frontend/views.py:411
+msgid "markup text"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:412
+msgid "other text"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:413
+msgid "image"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:414
+msgid "audio"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:415
+msgid "video"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:416
+msgid "other"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:417
+msgid "unknown"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:418
 msgid "Filter"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:791 MoinMoin/apps/frontend/views.py:792
+#: MoinMoin/apps/frontend/views.py:663 MoinMoin/config/default.py:337
+#: MoinMoin/templates/index.html:71 MoinMoin/templates/index.html:81
+msgid "Global Index"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:684 MoinMoin/apps/frontend/views.py:685
 msgid "My Changes"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:823
-#, python-format
-msgid "Items which refer to '%(item_name)s'"
+#: MoinMoin/apps/frontend/views.py:716
+msgid "Refers Here"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:894 MoinMoin/config/default.py:334
+#: MoinMoin/apps/frontend/views.py:790 MoinMoin/config/default.py:336
 #: MoinMoin/templates/global_history.html:10
 msgid "Global History"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:957 MoinMoin/apps/frontend/views.py:976
+#: MoinMoin/apps/frontend/views.py:853 MoinMoin/apps/frontend/views.py:872
 #, python-format
 msgid "You must login to use this action: %(action)s."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:960
+#: MoinMoin/apps/frontend/views.py:856
 msgid "A quicklink to this page could not be added for you."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:963
+#: MoinMoin/apps/frontend/views.py:859
 msgid "Your quicklink to this page could not be removed."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:978
+#: MoinMoin/apps/frontend/views.py:874
 msgid "You are not allowed to subscribe to an item you may not read."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:982
+#: MoinMoin/apps/frontend/views.py:878
 msgid "Can't remove regular expression subscription!"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:983
+#: MoinMoin/apps/frontend/views.py:879
 msgid "Edit the subscription regular expressions in your settings."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:987
+#: MoinMoin/apps/frontend/views.py:883
 msgid "You could not get subscribed to this item."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:996 MoinMoin/apps/frontend/views.py:1189
-#: MoinMoin/apps/frontend/views.py:1322
+#: MoinMoin/apps/frontend/views.py:892 MoinMoin/apps/frontend/views.py:1086
+#: MoinMoin/apps/frontend/views.py:1223
 msgid "The passwords do not match."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1012 MoinMoin/apps/frontend/views.py:1147
-#: MoinMoin/apps/frontend/views.py:1207 MoinMoin/apps/frontend/views.py:1274
-#: MoinMoin/apps/frontend/views.py:1387 MoinMoin/converter/archive_in.py:59
-#: MoinMoin/templates/history.html:24
+#: MoinMoin/apps/frontend/views.py:908 MoinMoin/apps/frontend/views.py:924
+#: MoinMoin/apps/frontend/views.py:1044 MoinMoin/apps/frontend/views.py:1104
+#: MoinMoin/apps/frontend/views.py:1171 MoinMoin/apps/frontend/views.py:1289
+#: MoinMoin/templates/history.html:20
 msgid "Name"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1012 MoinMoin/apps/frontend/views.py:1387
+#: MoinMoin/apps/frontend/views.py:908 MoinMoin/apps/frontend/views.py:924
+#: MoinMoin/apps/frontend/views.py:1289
 msgid "The login name you want to use"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1013 MoinMoin/apps/frontend/views.py:1209
-#: MoinMoin/apps/frontend/views.py:1348
+#: MoinMoin/apps/frontend/views.py:909 MoinMoin/apps/frontend/views.py:910
+#: MoinMoin/apps/frontend/views.py:925 MoinMoin/apps/frontend/views.py:926
+#: MoinMoin/apps/frontend/views.py:1172
+msgid "Password"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:909 MoinMoin/apps/frontend/views.py:925
+#: MoinMoin/apps/frontend/views.py:1106 MoinMoin/apps/frontend/views.py:1247
 msgid "The login password you want to use"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1014 MoinMoin/apps/frontend/views.py:1210
-#: MoinMoin/apps/frontend/views.py:1349
+#: MoinMoin/apps/frontend/views.py:910 MoinMoin/apps/frontend/views.py:926
+#: MoinMoin/apps/frontend/views.py:1107 MoinMoin/apps/frontend/views.py:1248
 msgid "Repeat the same password"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1017 MoinMoin/apps/frontend/views.py:1056
+#: MoinMoin/apps/frontend/views.py:911 MoinMoin/apps/frontend/views.py:928
+#: MoinMoin/apps/frontend/views.py:1045 MoinMoin/apps/frontend/views.py:1255
+msgid "E-Mail"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:911 MoinMoin/apps/frontend/views.py:928
+#: MoinMoin/apps/frontend/views.py:1045 MoinMoin/apps/frontend/views.py:1255
+msgid "Your E-Mail address"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:912 MoinMoin/apps/frontend/views.py:929
+#: MoinMoin/apps/frontend/views.py:1173 MoinMoin/apps/frontend/views.py:1291
+msgid "OpenID"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:912 MoinMoin/apps/frontend/views.py:929
+#: MoinMoin/apps/frontend/views.py:1291
+msgid "Your OpenID address"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:913 MoinMoin/apps/frontend/views.py:960
+#: MoinMoin/templates/openid_register.html:21
 msgid "Register"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1100
-msgid ""
-"Account verification required, please see the email we sent to your "
-"address."
+#: MoinMoin/apps/frontend/views.py:991 MoinMoin/apps/frontend/views.py:1017
+msgid "Account created, please log in now."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1102
-#, python-format
-msgid ""
-"An error occurred while sending the verification email: \"%(message)s\" "
-"Please contact an administrator to activate your account."
+#: MoinMoin/apps/frontend/views.py:1029
+msgid "Your user name or your email address is needed."
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1044 MoinMoin/apps/frontend/views.py:1104
+msgid "Your login name"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1046
+msgid "Recover password"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1054 MoinMoin/templates/lostpass.html:5
+msgid "Lost Password"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1076
+msgid "If this account exists, you will be notified."
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1087 MoinMoin/apps/frontend/views.py:1225
+msgid "New password is unacceptable, encoding trouble."
 msgstr ""
 
 #: MoinMoin/apps/frontend/views.py:1105
-msgid "Account created, please log in now."
+msgid "Recovery token"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1123
-msgid "Your account has been activated, you can log in now."
+#: MoinMoin/apps/frontend/views.py:1105
+msgid "The recovery token that has been sent to you"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1125 MoinMoin/apps/frontend/views.py:1234
+#: MoinMoin/apps/frontend/views.py:1106 MoinMoin/apps/frontend/views.py:1247
+msgid "New password"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1107 MoinMoin/apps/frontend/views.py:1248
+msgid "New password (repeat)"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1108 MoinMoin/apps/frontend/views.py:1249
+#: MoinMoin/templates/usersettings.html:9
+msgid "Change password"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1116 MoinMoin/templates/recoverpass.html:5
+msgid "Recover Password"
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1129
+msgid "Your password has been changed, you can log in now."
+msgstr ""
+
+#: MoinMoin/apps/frontend/views.py:1131
 msgid "Your token is invalid!"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1132
-msgid "Your user name or your email address is needed."
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1147 MoinMoin/apps/frontend/views.py:1207
-msgid "Your login name"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1149
-msgid "Recover password"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1157 MoinMoin/templates/lostpass.html:5
-msgid "Lost Password"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1179
-msgid "If this account exists, you will be notified."
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1190 MoinMoin/apps/frontend/views.py:1324
-msgid "New password is unacceptable, encoding trouble."
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1208
-msgid "Recovery token"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1208
-msgid "The recovery token that has been sent to you"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1209 MoinMoin/apps/frontend/views.py:1348
-msgid "New password"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1210 MoinMoin/apps/frontend/views.py:1349
-msgid "New password (repeat)"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1211 MoinMoin/apps/frontend/views.py:1350
-#: MoinMoin/templates/usersettings_forms.html:28
-msgid "Change password"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1219 MoinMoin/templates/recoverpass.html:5
-msgid "Recover Password"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1232
-msgid "Your password has been changed, you can log in now."
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1246
+#: MoinMoin/apps/frontend/views.py:1143
 msgid "Either your username or password was invalid."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1247
+#: MoinMoin/apps/frontend/views.py:1144
 msgid "Failed to authenticate with this OpenID."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1277
-msgid "Log in"
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1285 MoinMoin/templates/layout.html:63
+#: MoinMoin/apps/frontend/views.py:1184 MoinMoin/templates/layout.html:61
 msgid "Login"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1314
+#: MoinMoin/apps/frontend/views.py:1213
 msgid "You are now logged out."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1323
+#: MoinMoin/apps/frontend/views.py:1224
 msgid "The current password was wrong."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1347
+#: MoinMoin/apps/frontend/views.py:1246
 msgid "Current Password"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1347
+#: MoinMoin/apps/frontend/views.py:1246
 msgid "Your current login password"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1355 MoinMoin/apps/frontend/views.py:1361
-#: MoinMoin/apps/frontend/views.py:1376 MoinMoin/apps/frontend/views.py:1398
-#: MoinMoin/apps/frontend/views.py:1409
-#: MoinMoin/templates/usersettings_forms.html:14
-#: MoinMoin/templates/usersettings_forms.html:42
-#: MoinMoin/templates/usersettings_forms.html:56
-#: MoinMoin/templates/usersettings_forms.html:67
-#: MoinMoin/templates/usersettings_forms.html:81
+#: MoinMoin/apps/frontend/views.py:1256 MoinMoin/apps/frontend/views.py:1262
+#: MoinMoin/apps/frontend/views.py:1277 MoinMoin/apps/frontend/views.py:1300
+#: MoinMoin/apps/frontend/views.py:1311
 msgid "Save"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1372
+#: MoinMoin/apps/frontend/views.py:1273
 msgid "Publish my email (not my wiki homepage) in author info"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1373
+#: MoinMoin/apps/frontend/views.py:1274
 msgid "Open editor on double click"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1374
+#: MoinMoin/apps/frontend/views.py:1275
 msgid "Show comment sections"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1375
+#: MoinMoin/apps/frontend/views.py:1276
 msgid "Disable this account forever"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1382 MoinMoin/templates/usersettings.html:5
+#: MoinMoin/apps/frontend/views.py:1284 MoinMoin/templates/usersettings.html:6
 msgid "User Settings"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1388
+#: MoinMoin/apps/frontend/views.py:1290
 msgid "Alias-Name"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1388
+#: MoinMoin/apps/frontend/views.py:1290
 msgid "Your alias name (informational)"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1392
+#: MoinMoin/apps/frontend/views.py:1294
 msgid "Timezone"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1397
+#: MoinMoin/apps/frontend/views.py:1299
 msgid "Locale"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1405
+#: MoinMoin/apps/frontend/views.py:1307
 msgid "Theme name"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1406
+#: MoinMoin/apps/frontend/views.py:1308
 msgid "User CSS URL"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1406
+#: MoinMoin/apps/frontend/views.py:1308
 msgid "Give the URL of your custom CSS (optional)"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1407
+#: MoinMoin/apps/frontend/views.py:1309
 msgid "Editor size"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1407
+#: MoinMoin/apps/frontend/views.py:1309
 msgid "Editor textarea height (0=auto)"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1408
+#: MoinMoin/apps/frontend/views.py:1310
 msgid "History results per page"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1408
+#: MoinMoin/apps/frontend/views.py:1310
 msgid "Number of results per page (0=no paging)"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1451
+#: MoinMoin/apps/frontend/views.py:1339
 msgid "Your password has been changed."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1456
+#: MoinMoin/apps/frontend/views.py:1344
 msgid "This openid is already in use."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1460
+#: MoinMoin/apps/frontend/views.py:1348
 msgid "This username is already in use."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1466
+#: MoinMoin/apps/frontend/views.py:1354
 msgid "This email is already in use"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1481
-msgid ""
-"Your account has been disabled because you changed your email address. "
-"Please see the email we sent to your address to reactivate it."
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1488
-msgid ""
-"Your email address was not changed because sending the verification email"
-" failed. Please try again later."
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1494
-msgid "Your changes have been saved."
-msgstr ""
-
-#: MoinMoin/apps/frontend/views.py:1546
+#: MoinMoin/apps/frontend/views.py:1392
 msgid "You must log in to use bookmarks."
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1672
-#, python-format
-msgid "Items with similar names to '%(item_name)s'"
+#: MoinMoin/apps/frontend/views.py:1501 MoinMoin/config/default.py:380
+msgid "Items with similar names"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1856 MoinMoin/apps/frontend/views.py:1883
+#: MoinMoin/apps/frontend/views.py:1680 MoinMoin/apps/frontend/views.py:1707
 msgid "All tags in this wiki"
 msgstr ""
 
-#: MoinMoin/apps/frontend/views.py:1897
+#: MoinMoin/apps/frontend/views.py:1721
 #, python-format
 msgid "Items tagged with %(tag)s"
 msgstr ""
 
-#: MoinMoin/auth/__init__.py:244 MoinMoin/auth/ldap_login.py:130
+#: 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:252 MoinMoin/auth/ldap_login.py:200
-#: MoinMoin/auth/ldap_login.py:248
+#: 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:256
+#: 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 ""
 
-#: MoinMoin/auth/__init__.py:258
+#: MoinMoin/auth/__init__.py:256
 #, python-format
 msgid "<a href=\"%(recover_url)s\">Forgot your password?</a>"
 msgstr ""
 
-#: MoinMoin/auth/http.py:59
+#: MoinMoin/auth/http.py:61
 msgid "Please log in first."
 msgstr ""
 
-#: MoinMoin/auth/ldap_login.py:262
+#: MoinMoin/auth/ldap_login.py:259
 #, python-format
 msgid "LDAP server %(server)s failed."
 msgstr ""
@@ -728,23 +679,23 @@
 msgid "OpenID verification cancelled."
 msgstr ""
 
-#: MoinMoin/auth/openidrp.py:105
+#: MoinMoin/auth/openidrp.py:106
 msgid "This OpenID provider is not trusted."
 msgstr ""
 
-#: MoinMoin/auth/openidrp.py:110
+#: MoinMoin/auth/openidrp.py:111
 msgid "OpenID failure."
 msgstr ""
 
-#: MoinMoin/auth/openidrp.py:144
+#: MoinMoin/auth/openidrp.py:145
 msgid "Failed to resolve OpenID."
 msgstr ""
 
-#: MoinMoin/auth/openidrp.py:146
+#: MoinMoin/auth/openidrp.py:147
 msgid "OpenID discovery failure, not a valid OpenID."
 msgstr ""
 
-#: MoinMoin/auth/openidrp.py:150
+#: MoinMoin/auth/openidrp.py:151
 msgid "No OpenID service at this URL."
 msgstr ""
 
@@ -766,73 +717,69 @@
 msgid "Password is too easy to guess (keyboard sequence)."
 msgstr ""
 
-#: MoinMoin/config/default.py:333
+#: MoinMoin/config/default.py:335
 msgid "Home"
 msgstr ""
 
-#: MoinMoin/config/default.py:333
+#: MoinMoin/config/default.py:335
 msgid "Home Page"
 msgstr ""
 
-#: MoinMoin/config/default.py:334 MoinMoin/config/default.py:363
+#: MoinMoin/config/default.py:336 MoinMoin/config/default.py:364
 msgid "History"
 msgstr ""
 
-#: MoinMoin/config/default.py:335 MoinMoin/config/default.py:368
+#: MoinMoin/config/default.py:337 MoinMoin/config/default.py:369
 msgid "Index"
 msgstr ""
 
-#: MoinMoin/config/default.py:335 MoinMoin/templates/index.html:7
-#: MoinMoin/templates/index.html:71
-msgid "Global Index"
+#: MoinMoin/config/default.py:338
+msgid "Tags"
 msgstr ""
 
-#: MoinMoin/config/default.py:336
+#: MoinMoin/config/default.py:338
 msgid "Global Tags Index"
 msgstr ""
 
-#: MoinMoin/config/default.py:338
+#: MoinMoin/config/default.py:339 MoinMoin/templates/index.html:50
+#: MoinMoin/templates/index.html:123
+msgid "More"
+msgstr ""
+
+#: MoinMoin/config/default.py:339
 msgid "Administration & Docs"
 msgstr ""
 
-#: MoinMoin/config/default.py:361
+#: MoinMoin/config/default.py:362
 msgid "Show"
 msgstr ""
 
-#: MoinMoin/config/default.py:362 MoinMoin/templates/index.html:34
+#: MoinMoin/config/default.py:363 MoinMoin/templates/index.html:27
 msgid "Download"
 msgstr ""
 
-#: MoinMoin/config/default.py:363
+#: MoinMoin/config/default.py:364
 msgid "Revision History"
 msgstr ""
 
-#: MoinMoin/config/default.py:366 MoinMoin/templates/show.html:9
+#: MoinMoin/config/default.py:367 MoinMoin/templates/show.html:9
 msgid "Modify"
 msgstr ""
 
-#: MoinMoin/config/default.py:366
+#: MoinMoin/config/default.py:367
 msgid "Edit or Upload"
 msgstr ""
 
-#: MoinMoin/config/default.py:368
+#: MoinMoin/config/default.py:369
 msgid "List sub-items"
 msgstr ""
 
-#: MoinMoin/config/default.py:369
+#: MoinMoin/config/default.py:370
 msgid "Comments"
 msgstr ""
 
-#: MoinMoin/config/default.py:369 MoinMoin/templates/common.js:89
-msgid "Hide comments"
-msgstr ""
-
 #: MoinMoin/config/default.py:370
-msgid "Transclusions"
-msgstr ""
-
-#: MoinMoin/config/default.py:370 MoinMoin/templates/common.js:123
-msgid "Show transclusions"
+msgid "Switch showing comments on or off"
 msgstr ""
 
 #: MoinMoin/config/default.py:371
@@ -867,7 +814,7 @@
 msgid "Rename this item"
 msgstr ""
 
-#: MoinMoin/config/default.py:376 MoinMoin/templates/index.html:38
+#: MoinMoin/config/default.py:376 MoinMoin/templates/index.html:31
 msgid "Delete"
 msgstr ""
 
@@ -875,7 +822,7 @@
 msgid "Delete this item"
 msgstr ""
 
-#: MoinMoin/config/default.py:377 MoinMoin/templates/index.html:43
+#: MoinMoin/config/default.py:377 MoinMoin/templates/index.html:36
 msgid "Destroy"
 msgstr ""
 
@@ -903,64 +850,20 @@
 msgid "Similar"
 msgstr ""
 
-#: MoinMoin/config/default.py:380
-msgid "Items with similar names"
-msgstr ""
-
-#: MoinMoin/constants/itemtypes.py:29
-msgid "Wiki item"
-msgstr ""
-
-#: MoinMoin/constants/itemtypes.py:30
-msgid "Ticket"
-msgstr ""
-
-#: MoinMoin/constants/itemtypes.py:30
-msgid "Ticket item"
-msgstr ""
-
-#: MoinMoin/constants/itemtypes.py:31
-msgid "Blog"
-msgstr ""
-
-#: MoinMoin/constants/itemtypes.py:31
-msgid "Blog item"
-msgstr ""
-
-#: MoinMoin/constants/itemtypes.py:32
-msgid "Blog entry"
-msgstr ""
-
-#: MoinMoin/constants/itemtypes.py:32
-msgid "Blog entry item"
-msgstr ""
-
-#: MoinMoin/converter/archive_in.py:59
-msgid "Date"
-msgstr ""
-
-#: MoinMoin/converter/html_out.py:539
-msgid "Link to this heading"
-msgstr ""
-
-#: MoinMoin/converter/html_out.py:605
+#: MoinMoin/converter/html_out.py:591
 msgid "Contents"
 msgstr ""
 
-#: MoinMoin/converter/macro.py:81
-msgid "Error: invalid macro name."
-msgstr ""
-
-#: MoinMoin/converter/macro.py:91 MoinMoin/converter/macro_Verbatim.py:68
+#: MoinMoin/converter/macro.py:84
 #, python-format
 msgid "<<%(macro_name)s: execution failed [%(error_msg)s] (see also the log)>>"
 msgstr ""
 
-#: MoinMoin/converter/moinwiki_in.py:1034
+#: MoinMoin/converter/moinwiki_in.py:1035
 msgid "Error:"
 msgstr ""
 
-#: MoinMoin/converter/moinwiki_in.py:1035
+#: MoinMoin/converter/moinwiki_in.py:1036
 msgid "is invalid within"
 msgstr ""
 
@@ -969,165 +872,68 @@
 msgid "%(item_name)s does not exist. Create it?"
 msgstr ""
 
-#: MoinMoin/items/__init__.py:125 MoinMoin/templates/atom.html:12
-#: MoinMoin/templates/diff_text_atom.html:13 MoinMoin/templates/history.html:31
-msgid "Comment"
-msgstr ""
-
-#: MoinMoin/items/__init__.py:125
-msgid "Comment about your change"
-msgstr ""
-
-#: MoinMoin/items/__init__.py:130
-msgid "Item type"
-msgstr ""
-
-#: MoinMoin/items/__init__.py:131
-msgid "Content type"
-msgstr ""
-
-#: MoinMoin/items/__init__.py:135 MoinMoin/items/ticket.py:40
-msgid "Summary"
-msgstr ""
-
-#: MoinMoin/items/__init__.py:135 MoinMoin/items/ticket.py:40
-msgid "One-line summary of the item"
+#: MoinMoin/items/__init__.py:660
+msgid "Invalid JSON."
 msgstr ""
 
-#: MoinMoin/items/__init__.py:331
-msgid "Extra MetaData (JSON)"
-msgstr ""
-
-#: MoinMoin/items/blog.py:31
-msgid "Supertags (Categories)"
-msgstr ""
-
-#: MoinMoin/items/blog.py:32
-msgid "Ordered comma separated list of tags"
+#: MoinMoin/items/__init__.py:704 MoinMoin/items/__init__.py:1151
+#: MoinMoin/items/__init__.py:1318 MoinMoin/items/__init__.py:1408
+#: MoinMoin/items/__init__.py:1494
+msgid "MetaData (JSON)"
 msgstr ""
 
-#: MoinMoin/items/blog.py:35
-msgid "Title"
-msgstr ""
-
-#: MoinMoin/items/blog.py:36
-msgid "One-line title of the blog entry"
-msgstr ""
-
-#: MoinMoin/items/blog.py:37
-msgid "Publication time (UTC)"
-msgstr ""
-
-#: MoinMoin/items/content.py:278
+#: MoinMoin/items/__init__.py:705 MoinMoin/items/__init__.py:1153
+#: MoinMoin/items/__init__.py:1319 MoinMoin/items/__init__.py:1409
+#: MoinMoin/items/__init__.py:1495
 msgid "Upload file:"
 msgstr ""
 
-#: MoinMoin/items/content.py:296
+#: MoinMoin/items/__init__.py:731
 msgid ""
 "The items have the same data hash code (that means they very likely have "
 "the same data)."
 msgstr ""
 
-#: MoinMoin/items/content.py:298
+#: MoinMoin/items/__init__.py:733
 msgid "The items have different data."
 msgstr ""
 
-#: MoinMoin/items/content.py:310
+#: MoinMoin/items/__init__.py:740
 #, python-format
 msgid "Impossible to convert the data to the contenttype: %(contenttype)s"
 msgstr ""
 
-#: MoinMoin/items/content.py:697
+#: MoinMoin/items/__init__.py:1152
 msgid "Type your text here"
 msgstr ""
 
-#: MoinMoin/items/content.py:933 MoinMoin/items/content.py:1001
+#: MoinMoin/items/__init__.py:1348 MoinMoin/items/__init__.py:1443
 #, python-format
 msgid "Edit drawing %(filename)s (opens in new window)"
 msgstr ""
 
-#: MoinMoin/items/content.py:948 MoinMoin/items/content.py:1018
+#: MoinMoin/items/__init__.py:1363 MoinMoin/items/__init__.py:1460
 #, python-format
 msgid "Clickable drawing: %(filename)s"
 msgstr ""
 
-#: MoinMoin/items/ticket.py:41
-msgid "Effort"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:42
-msgid "Difficulty"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:43
-msgid "Severity"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:44
-msgid "Priority"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:46
-msgid "Assigned To"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:47
-msgid "Superseded By"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:48
-msgid "Depends On"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:51
-msgid "Supersedes"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:52
-msgid "Required By"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:53
-msgid "Subscribers"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:64
-msgid "Message"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:65
-msgid "Update ticket"
-msgstr ""
-
-#: MoinMoin/items/ticket.py:134
-msgid "Submit ticket"
-msgstr ""
-
-#: MoinMoin/mail/sendmail.py:94
+#: MoinMoin/mail/sendmail.py:86
 msgid "No recipients, nothing to do"
 msgstr ""
 
-#: MoinMoin/mail/sendmail.py:170
+#: MoinMoin/mail/sendmail.py:161
 #, python-format
 msgid "Connection to mailserver '%(server)s' failed: %(reason)s"
 msgstr ""
 
-#: MoinMoin/mail/sendmail.py:184
+#: MoinMoin/mail/sendmail.py:175
 msgid "Mail not sent"
 msgstr ""
 
-#: MoinMoin/mail/sendmail.py:187
+#: MoinMoin/mail/sendmail.py:178
 msgid "Mail sent successfully"
 msgstr ""
 
-#: MoinMoin/search/__init__.py:17
-msgid "Search query too short."
-msgstr ""
-
-#: MoinMoin/search/__init__.py:30 MoinMoin/templates/search.html:8
-msgid "Search"
-msgstr ""
-
 #: MoinMoin/security/textcha.py:158
 msgid "The entered TextCha was incorrect."
 msgstr ""
@@ -1145,30 +951,17 @@
 msgid "The item '%(item_name)s' does not exist."
 msgstr ""
 
-#: MoinMoin/templates/atom.html:4 MoinMoin/templates/diff_text_atom.html:5
-#: MoinMoin/templates/meta.html:12
-msgid "Revision"
-msgstr ""
-
-#: MoinMoin/templates/common.js:92
-msgid "Show comments"
-msgstr ""
-
-#: MoinMoin/templates/common.js:126
-msgid "Hide transclusions"
-msgstr ""
-
 #: MoinMoin/templates/delete.html:4
 #, python-format
 msgid "Delete '%(item_name)s'"
 msgstr ""
 
-#: MoinMoin/templates/destroy.html:4
+#: MoinMoin/templates/destroy.html:5
 #, python-format
 msgid "DESTROY COMPLETE item '%(item_name)s'"
 msgstr ""
 
-#: MoinMoin/templates/destroy.html:20
+#: MoinMoin/templates/destroy.html:17
 #, python-format
 msgid "DESTROY REVISION '%(item_name)s' (rev %(rev_id)s)"
 msgstr ""
@@ -1190,124 +983,120 @@
 msgid "Line"
 msgstr ""
 
-#: MoinMoin/templates/forms.html:215
+#: MoinMoin/templates/forms.html:80
 msgid "Upload"
 msgstr ""
 
-#: MoinMoin/templates/forms.html:216
+#: MoinMoin/templates/forms.html:81
 msgid "Upload files"
 msgstr ""
 
-#: MoinMoin/templates/forms.html:221
+#: MoinMoin/templates/forms.html:86
 msgid "Start All"
 msgstr ""
 
-#: MoinMoin/templates/forms.html:222
+#: MoinMoin/templates/forms.html:87
 msgid "Cancel All"
 msgstr ""
 
-#: MoinMoin/templates/forms.html:226
+#: MoinMoin/templates/forms.html:91
 msgid "Start"
 msgstr ""
 
-#: MoinMoin/templates/forms.html:227 MoinMoin/templates/index.html:143
+#: MoinMoin/templates/forms.html:92 MoinMoin/templates/index.html:143
 #: MoinMoin/templates/index.html:161
 msgid "Cancel"
 msgstr ""
 
-#: MoinMoin/templates/global_history.html:19
+#: MoinMoin/templates/global_history.html:20
 msgid "Set bookmark"
 msgstr ""
 
-#: MoinMoin/templates/global_history.html:49
+#: MoinMoin/templates/global_history.html:51
 msgid "Delete bookmark"
 msgstr ""
 
-#: MoinMoin/templates/highlight.html:3
+#: MoinMoin/templates/highlight.html:4
 #, python-format
-msgid "Syntax highlighting of '%(name)s'"
+msgid "Syntax highlighting for Item: %(name)s"
 msgstr ""
 
-#: MoinMoin/templates/history.html:4
+#: MoinMoin/templates/history.html:8
+msgid "Previous"
+msgstr ""
+
+#: MoinMoin/templates/history.html:11
+msgid "Next"
+msgstr ""
+
+#: MoinMoin/templates/history.html:14
 #, python-format
 msgid "History of '%(item_name)s'"
 msgstr ""
 
-#: MoinMoin/templates/history.html:11
-msgid "Previous"
-msgstr ""
-
-#: MoinMoin/templates/history.html:14
-msgid "Next"
-msgstr ""
-
-#: MoinMoin/templates/history.html:25
+#: MoinMoin/templates/history.html:21
 msgid "Rev."
 msgstr ""
 
-#: MoinMoin/templates/history.html:26
+#: MoinMoin/templates/history.html:22
 msgid "Timestamp"
 msgstr ""
 
-#: MoinMoin/templates/history.html:29
+#: MoinMoin/templates/history.html:25
 msgid "Editor"
 msgstr ""
 
-#: MoinMoin/templates/history.html:30
+#: MoinMoin/templates/history.html:26
 msgid "Content-Type"
 msgstr ""
 
-#: MoinMoin/templates/history.html:51
+#: MoinMoin/templates/history.html:45
 msgid "show"
 msgstr ""
 
-#: MoinMoin/templates/history.html:52
+#: MoinMoin/templates/history.html:46
 msgid "meta"
 msgstr ""
 
-#: MoinMoin/templates/history.html:53
+#: MoinMoin/templates/history.html:47
 msgid "download"
 msgstr ""
 
-#: MoinMoin/templates/history.html:54
+#: MoinMoin/templates/history.html:48
 msgid "highlight"
 msgstr ""
 
-#: MoinMoin/templates/history.html:56
+#: MoinMoin/templates/history.html:50
 msgid "revert"
 msgstr ""
 
-#: MoinMoin/templates/history.html:59
+#: MoinMoin/templates/history.html:53
 msgid "destroy"
 msgstr ""
 
-#: MoinMoin/templates/index.html:5
-#, python-format
-msgid "Index of subitems of '%(item_name)s'"
-msgstr ""
-
-#: MoinMoin/templates/index.html:22
+#: MoinMoin/templates/index.html:16
 msgid "Select All"
 msgstr ""
 
-#: MoinMoin/templates/index.html:32
+#: MoinMoin/templates/index.html:26
 msgid "New item"
 msgstr ""
 
-#: MoinMoin/templates/index.html:51
+#: MoinMoin/templates/index.html:44
 msgid "Filter by content type"
 msgstr ""
 
-#: MoinMoin/templates/index.html:52
+#: MoinMoin/templates/index.html:45
 msgid "items having unknown mime types"
 msgstr ""
 
-#: MoinMoin/templates/index.html:56
+#: MoinMoin/templates/index.html:49
 msgid "Toggle"
 msgstr ""
 
-#: MoinMoin/templates/index.html:57 MoinMoin/templates/index.html:123
-msgid "More"
+#: MoinMoin/templates/index.html:69
+#, python-format
+msgid "Index of subitems of '%(item_name)s'"
 msgstr ""
 
 #: MoinMoin/templates/index.html:86
@@ -1350,56 +1139,27 @@
 msgid "(Drag and drop multiple files to this white area to upload them.)"
 msgstr ""
 
-#: MoinMoin/templates/index_action.js:19
-msgid "Deleting.."
-msgstr ""
-
-#: MoinMoin/templates/index_action.js:19
-msgid "Destroying.."
-msgstr ""
-
-#: MoinMoin/templates/index_action.js:20
-msgid "Items deleted: "
-msgstr ""
-
-#: MoinMoin/templates/index_action.js:20
-msgid "Items destroyed: "
-msgstr ""
-
-#: MoinMoin/templates/index_action.js:21
-msgid ", Items not deleted: "
-msgstr ""
-
-#: MoinMoin/templates/index_action.js:21
-msgid ", Items not destroyed: "
-msgstr ""
-
-#: MoinMoin/templates/index_action.js:186
-#: MoinMoin/templates/index_action.js:207
-msgid "Nothing was selected."
-msgstr ""
-
-#: MoinMoin/templates/itemviews.html:49
+#: MoinMoin/templates/itemviews.html:43
 msgid "Remove Link"
 msgstr ""
 
-#: MoinMoin/templates/itemviews.html:51
+#: MoinMoin/templates/itemviews.html:45
 msgid "Add Link"
 msgstr ""
 
-#: MoinMoin/templates/itemviews.html:61
+#: MoinMoin/templates/itemviews.html:54
 msgid "Unsubscribe"
 msgstr ""
 
-#: MoinMoin/templates/itemviews.html:63
+#: MoinMoin/templates/itemviews.html:56
 msgid "Subscribe"
 msgstr ""
 
-#: MoinMoin/templates/layout.html:51
+#: MoinMoin/templates/layout.html:49
 msgid "Settings"
 msgstr ""
 
-#: MoinMoin/templates/layout.html:57
+#: MoinMoin/templates/layout.html:55
 msgid "Logout"
 msgstr ""
 
@@ -1407,90 +1167,64 @@
 msgid "Moin login"
 msgstr ""
 
-#: MoinMoin/templates/login.html:25
-msgid "OpenID login"
+#: MoinMoin/templates/login.html:21 MoinMoin/templates/login.html:35
+msgid "Log in"
 msgstr ""
 
-#: MoinMoin/templates/lookup.html:30
-msgid "No results"
-msgstr ""
-
-#: MoinMoin/templates/lookup.html:32
-msgid "More than one result"
+#: MoinMoin/templates/login.html:28
+msgid "OpenID login"
 msgstr ""
 
 #: MoinMoin/templates/lostpass.html:7
 msgid "Please note that you only need to fill out one form field."
 msgstr ""
 
-#: MoinMoin/templates/meta.html:3
+#: MoinMoin/templates/meta.html:9
 #, python-format
 msgid "Metadata of '%(item_name)s'"
 msgstr ""
 
-#: MoinMoin/templates/modify.html:13
-#, python-format
-msgid "Modifying '%(item_name)s'"
-msgstr ""
-
-#: MoinMoin/templates/modify.html:27
-msgid "Link to Subitem"
+#: MoinMoin/templates/meta.html:10
+msgid "Revision"
 msgstr ""
 
-#: MoinMoin/templates/modify.html:27
-msgid "Link"
-msgstr ""
-
-#: MoinMoin/templates/modify.html:30
-msgid "Transclude Subitem"
-msgstr ""
-
-#: MoinMoin/templates/modify.html:30
-msgid "Transclude"
-msgstr ""
-
-#: MoinMoin/templates/modify_anywikidraw.html:17
-#: MoinMoin/templates/modify_twikidraw.html:12
+#: MoinMoin/templates/modify_anywikidraw.html:18
+#: MoinMoin/templates/modify_twikidraw.html:13
 msgid "NOTE:"
 msgstr ""
 
-#: MoinMoin/templates/modify_anywikidraw.html:17
-#: MoinMoin/templates/modify_twikidraw.html:12
+#: MoinMoin/templates/modify_anywikidraw.html:18
+#: MoinMoin/templates/modify_twikidraw.html:13
 msgid "You need a Java enabled browser to edit the drawing."
 msgstr ""
 
-#: MoinMoin/templates/modify_select_contenttype.html:3
-#: MoinMoin/templates/modify_select_template.html:3
+#: MoinMoin/templates/modify_applet.html:4
+#, python-format
+msgid "Modifying %(item_name)s"
+msgstr ""
+
+#: MoinMoin/templates/modify_show_template_selection.html:3
+#: MoinMoin/templates/modify_show_type_selection.html:3
 msgid "Create new item?"
 msgstr ""
 
-#: MoinMoin/templates/modify_select_contenttype.html:6
-#, python-format
-msgid "Please select the contenttype of the new %(itemtype)s item."
-msgstr ""
-
-#: MoinMoin/templates/modify_select_itemtype.html:3
-msgid "Item not found, create it now?"
-msgstr ""
-
-#: MoinMoin/templates/modify_select_itemtype.html:5
-#, python-format
-msgid ""
-"Item '%(name)s' 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/modify_select_template.html:5
+#: 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 ""
 
-#: MoinMoin/templates/modify_select_template.html:8
+#: 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 ""
@@ -1505,37 +1239,32 @@
 msgid "Revert '%(item_name)s' (rev %(rev_id)s)"
 msgstr ""
 
-#: MoinMoin/templates/search.html:16
+#: MoinMoin/templates/search.html:7
 #, python-format
 msgid "%(result_len)d results found (%(runtime).3f secs)."
 msgstr ""
 
-#: MoinMoin/templates/search.html:21
+#: MoinMoin/templates/search.html:12
 #, python-format
 msgid "No results found (%(runtime).3f secs)."
 msgstr ""
 
-#: MoinMoin/templates/search.html:35
+#: MoinMoin/templates/search.html:26
 #, python-format
 msgid "name term suggestions: %(termlist)s"
 msgstr ""
 
-#: MoinMoin/templates/search.html:36
+#: MoinMoin/templates/search.html:27
 #, python-format
 msgid "content term suggestions: %(termlist)s"
 msgstr ""
 
-#: MoinMoin/templates/search.html:51 MoinMoin/templates/show.html:54
-#, python-format
-msgid "Summary: %(summary)s"
-msgstr ""
-
-#: MoinMoin/templates/search.html:57
+#: MoinMoin/templates/search.html:40
 #, python-format
 msgid "Revision: %(revid)s Last Change: %(mtime)s"
 msgstr ""
 
-#: MoinMoin/templates/search.html:65
+#: MoinMoin/templates/search.html:48
 msgid "You don't have read permission for this item."
 msgstr ""
 
@@ -1555,130 +1284,61 @@
 msgid "no newer revision"
 msgstr ""
 
-#: MoinMoin/templates/show.html:77
+#: MoinMoin/templates/show.html:68
 msgid "modified"
 msgstr ""
 
-#: MoinMoin/templates/show.html:78 MoinMoin/templates/blog/utils.html:22
+#: MoinMoin/templates/show.html:69
 msgid "by"
 msgstr ""
 
-#: MoinMoin/templates/show.html:79
+#: MoinMoin/templates/show.html:70
 msgid "tagged"
 msgstr ""
 
-#: MoinMoin/templates/show_nonexistent.html:3
-msgid "Item not found"
-msgstr ""
-
-#: MoinMoin/templates/show_nonexistent.html:5
-#, python-format
-msgid "Item '%(name)s' does not exist."
-msgstr ""
-
 #: MoinMoin/templates/sitemap.html:3
 #, python-format
-msgid "SiteMap of '%(item_name)s'"
-msgstr ""
-
-#: MoinMoin/templates/ticket.html:5
-#, python-format
-msgid "Creating new ticket: '%(item_name)s'"
-msgstr ""
-
-#: MoinMoin/templates/ticket.html:7
-#, python-format
-msgid "Ticket: '%(item_name)s'"
+msgid "SiteMap of %(item_name)s"
 msgstr ""
 
 #: MoinMoin/templates/usersettings.html:8
+#: MoinMoin/templates/usersettings.html:17
 msgid "Personal Settings"
 msgstr ""
 
-#: MoinMoin/templates/usersettings.html:13
-msgid "Change Password"
-msgstr ""
-
-#: MoinMoin/templates/usersettings.html:18
+#: MoinMoin/templates/usersettings.html:10
+#: MoinMoin/templates/usersettings.html:47
 msgid "Notification Settings"
 msgstr ""
 
-#: MoinMoin/templates/usersettings.html:23
+#: MoinMoin/templates/usersettings.html:11
+#: MoinMoin/templates/usersettings.html:59
 msgid "Wiki Appearance Settings"
 msgstr ""
 
-#: MoinMoin/templates/usersettings.html:28
+#: MoinMoin/templates/usersettings.html:12
+#: MoinMoin/templates/usersettings.html:74
 msgid "Navigation Settings"
 msgstr ""
 
-#: MoinMoin/templates/usersettings.html:33
+#: MoinMoin/templates/usersettings.html:13
+#: MoinMoin/templates/usersettings.html:86
 msgid "Options"
 msgstr ""
 
-#: MoinMoin/templates/usersettings_forms.html:35
-msgid ""
-"Changing your email address requires you to verify it. A link will be "
-"sent to you."
-msgstr ""
-
-#: MoinMoin/templates/utils.html:64
-msgid "Expand Subitem"
-msgstr ""
-
-#: MoinMoin/templates/utils.html:81
-msgid "Subitems"
-msgstr ""
-
-#: MoinMoin/templates/blog/main.html:4
-msgid "There are no entries in this blog."
-msgstr ""
-
-#: MoinMoin/templates/blog/main.html:6
-#, python-format
-msgid "There are no entries in the '%(tag)s' blog category."
-msgstr ""
-
-#: MoinMoin/templates/blog/main.html:13
-msgid "Category:"
+#: MoinMoin/templates/usersettings.html:33
+msgid "Change Password"
 msgstr ""
 
-#: MoinMoin/templates/blog/utils.html:15
-msgid "Modify entry"
-msgstr ""
-
-#: MoinMoin/templates/blog/utils.html:21
-msgid "Published on"
-msgstr ""
-
-#: MoinMoin/templates/blog/utils.html:24
-msgid "Not published yet."
-msgstr ""
-
-#: MoinMoin/templates/blog/utils.html:34
-msgid "Tags:"
-msgstr ""
-
-#: MoinMoin/templates/blog/utils.html:44
-msgid "Discussion page"
-msgstr ""
-
-#: MoinMoin/templates/blog/utils.html:51
-msgid "Categories"
-msgstr ""
-
-#: MoinMoin/templates/blog/utils.html:69
-msgid "only this blog"
-msgstr ""
-
-#: MoinMoin/themes/__init__.py:57
+#: MoinMoin/themes/__init__.py:54
 msgid "Access denied"
 msgstr ""
 
-#: MoinMoin/themes/__init__.py:58
+#: MoinMoin/themes/__init__.py:55
 msgid "You are not allowed to access this resource."
 msgstr ""
 
-#: MoinMoin/themes/__init__.py:292
+#: MoinMoin/themes/__init__.py:276
 msgid "anonymous"
 msgstr ""
 
--- a/MoinMoin/user.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/user.py	Sun Feb 10 19:55:14 2013 +0100
@@ -61,7 +61,8 @@
     if validate and search_users(name_exact=username):
         return _("This user name already belongs to somebody else.")
 
-    theuser.profile[NAME] = unicode(username)
+    # XXX currently we just support creating with 1 name:
+    theuser.profile[NAME] = [unicode(username), ]
 
     pw_checker = app.cfg.password_checker
     if validate and pw_checker:
@@ -112,6 +113,10 @@
 
 def search_users(**q):
     """ Searches for a users with given query keys/values """
+    # Since item name is a list, it's possible a list have been passed as parameter.
+    # No problem, since user always have just one name (TODO: validate single name for user)
+    if q.get('name_exact') and isinstance(q.get('name_exact'), list):
+        q['name_exact'] = q['name_exact'][0]
     q = update_user_query(**q)
     backend = get_user_backend()
     docs = backend.documents(**q)
@@ -133,7 +138,7 @@
         if userdata.mailto_author and userdata.email:
             return ('email', userdata.email)
         elif userdata.name:
-            interwiki = getInterwikiHome(userdata.name)
+            interwiki = getInterwikiHome(userdata.name0)
             if interwiki:
                 result = ('interwiki', interwiki)
     return result
@@ -276,6 +281,7 @@
         self.auth_method = kw.get('auth_method', 'internal')
         self.auth_attribs = kw.get('auth_attribs', ())
 
+        # XXX currently we just support creating with 1 name:
         _name = name or auth_username
 
         itemid = uid
@@ -292,7 +298,7 @@
         else:
             self.profile[ITEMID] = make_uuid()
             if _name:
-                self.profile[NAME] = _name
+                self.profile[NAME] = [_name, ]
             if password is not None:
                 self.set_password(password)
 
@@ -301,7 +307,7 @@
 
     def __repr__(self):
         # In rare cases we might not have these profile settings when the __repr__ is called.
-        name = getattr(self, NAME, None)
+        name = getattr(self, NAME, [])
         itemid = getattr(self, ITEMID, None)
 
         return "<{0}.{1} at {2:#x} name:{3!r} itemid:{4!r} valid:{5!r} trusted:{6!r}>".format(
@@ -321,6 +327,15 @@
             raise AttributeError(name)
 
     @property
+    def name0(self):
+        try:
+            names = self.name
+            assert isinstance(names, list)
+            return names[0]
+        except IndexError:
+            return u'anonymous'
+
+    @property
     def language(self):
         l = self._cfg.language_default
         locale = self.locale  # is either None or something like 'en_US'
@@ -669,7 +684,7 @@
 
     def is_current_user(self):
         """ Check if this user object is the user doing the current request """
-        return flaskg.user.name == self.name
+        return flaskg.user.itemid == self.itemid
 
     # Sessions ---------------------------------------------------
 
@@ -739,7 +754,7 @@
 If you didn't forget your password, please ignore this email.
 
 """, link=url_for('frontend.recoverpass',
-                        username=self.name, token=token, _external=True))
+                        username=self.name0, token=token, _external=True))
 
         subject = _('[%(sitename)s] Your wiki password recovery link',
                     sitename=self._cfg.sitename or "Wiki")
@@ -760,7 +775,7 @@
 If you didn't create this account, please ignore this email.
 
 """, link=url_for('frontend.verifyemail',
-                        username=self.name, token=token, _external=True))
+                        username=self.name0, token=token, _external=True))
 
         subject = _('[%(sitename)s] Please verify your email address',
                     sitename=self._cfg.sitename or "Wiki")
--- a/MoinMoin/util/_tests/test_interwiki.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/util/_tests/test_interwiki.py	Sun Feb 10 19:55:14 2013 +0100
@@ -14,33 +14,112 @@
 import os.path
 import shutil
 
-from MoinMoin.util.interwiki import split_interwiki, join_wiki, InterWikiMap
+from MoinMoin.util.interwiki import split_interwiki, join_wiki, InterWikiMap, url_for_item, _split_namespace
 from MoinMoin._tests import wikiconfig
+from MoinMoin.config import CURRENT
+from MoinMoin.app import before_wiki
 
+from flask import current_app as app
 
 class TestInterWiki(object):
     class Config(wikiconfig.Config):
-        interwiki_map = dict(Self='http://localhost:8080/', MoinMoin='http://moinmo.in/', )
+        interwiki_map = {'Self': 'http://localhost:8080/',
+                         'MoinMoin': 'http://moinmo.in/',
+                         'OtherWiki': 'http://otherwiki.com/',
+                         'OtherWiki:ns1': 'http://otherwiki.com/ns1/',
+                         'OtherWiki:ns1:ns2': 'http://otherwiki.com/ns1/ns2/'
+        }
 
-    def testSplitWiki(self):
-        tests = [('SomePage', ('Self', 'SomePage')),
-                 ('OtherWiki:OtherPage', ('OtherWiki', 'OtherPage')),
-                 (':OtherPage', ('', 'OtherPage')),
-                 # broken ('/OtherPage', ('Self', '/OtherPage')),
-                 # wrong interpretation ('MainPage/OtherPage', ('Self', 'MainPage/OtherPage')),
+    def test_url_for_item(self):
+        before_wiki()
+        revid = 'cdc431e0fc624d6fb8372152dcb66457'
+
+        tests = [(('SomePage', '', '', CURRENT, 'frontend.show_item', False), '/SomePage'),
+                 # Method signature to understand the tuple parameters
+                 # (item_name, wiki_name='', namespace='', rev=CURRENT, endpoint='frontend.show_item', _external=False):
+                 (('SomePage', '', '', CURRENT, 'frontend.show_item', True), 'http://localhost:8080/SomePage'),
+                 (('SomePage', '', '', CURRENT, 'frontend.modify_item', False), '/%2Bmodify/SomePage'),
+                 # FIXME if you set interwiki_map = dict(Self='http://localhost:8080', MoinMoin='http://moinmo.in/', ),
+                 # the above line make it fails, it returns http://localhost/%2Bmodify/SomePage
+                 # (('SomePage', '', '', CURRENT, 'frontend.modify_item', True), 'http://localhost:8080/%2Bmodify/SomePage'),
+                 (('SomePage', '', '', revid, 'frontend.show_item', False), '/%2Bshow/%2B{0}/SomePage'.format(revid)),
+                 (('SomePage', '', '', revid, 'frontend.show_item_meta', False), '/%2Bmeta/%2B{0}/SomePage'.format(revid)),
+                 # Valid namespaces
+                 (('SomePage', '', 'ns1', CURRENT, 'frontend.show_item', False), '/:ns1:SomePage'),
+                 (('SomePage', '', 'ns1:ns2', CURRENT, 'frontend.show_item', True), 'http://localhost:8080/:ns1:ns2:SomePage'),
+                 (('SomePage', '', 'ns1', CURRENT, 'frontend.modify_item', False), '/%2Bmodify/:ns1:SomePage'),
+                 (('SomePage', '', 'ns1:ns2', CURRENT, 'frontend.show_item_meta', True), 'http://localhost:8080/%2Bmeta/:ns1:ns2:SomePage'),
+                 (('SomePage', '', 'ns1', revid, 'frontend.show_item', False), '/%2Bshow/%2B{0}/:ns1:SomePage'.format(revid)),
+                 (('SomePage', '', 'ns1:ns2', revid, 'frontend.show_item_meta', False), '/%2Bmeta/%2B{0}/:ns1:ns2:SomePage'.format(revid)),
+
+                 (('SomePage', 'MoinMoin', 'ns1', CURRENT, 'frontend.show_item', False), 'http://moinmo.in/:ns1:SomePage'),
+                 (('SomePage', 'MoinMoin', '', CURRENT, 'frontend.show_item', False), 'http://moinmo.in/SomePage'),
+                 # FIXME will exist a map for this case? maybe there should be a placeholder for it.
+                 # we need that for wiki farms with common search index and search in non-current revisions.
+                 (('SomePage', 'MoinMoin', '', revid, 'frontend.show_item', False), 'http://moinmo.in/%2Bshow/%2B{0}/SomePage'.format(revid)),
+                 (('SomePage', 'non-existent', '', CURRENT, 'frontend.show_item', False), '/non-existent:SomePage'),
+                 (('SomePage', 'non-existent', 'ns1', CURRENT, 'frontend.show_item', False), '/non-existent:ns1:SomePage'),
                 ]
-        for markup, (wikiname, pagename) in tests:
-            assert split_interwiki(markup) == (wikiname, pagename)
+
+        for (item_name, wiki_name, namespace, rev, endpoint, _external), url in tests:
+            assert url_for_item(item_name, wiki_name, namespace, rev, endpoint, _external) == url
+
+    def test__split_namespace(self):
+        map = set()
+        map.add(u'ns1')
+        map.add(u'ns1:ns2')
+        tests = [('', ('', '')),
+                 ('OtherWiki:', ('', 'OtherWiki:')),
+                 ('ns1:', ('ns1', '')),
+                 ('ns3:foo', ('', 'ns3:foo')),
+                 ('ns1:OtherPage', ('ns1', 'OtherPage')),
+                 ('ns1:ns2:OtherPage', ('ns1:ns2', 'OtherPage')),
+                 ('ns1:ns2:ns1:ns2:OtherPage', ('ns1:ns2', 'ns1:ns2:OtherPage')),
+                 ('SomePage', ('', 'SomePage')),
+                 ('OtherWiki:ns1:OtherPage', ('', 'OtherWiki:ns1:OtherPage')),
+                ]
+        for markup, (namespace, pagename) in tests:
+            assert _split_namespace(map, markup) == (namespace, pagename)
+            namespace, pagename = _split_namespace(map, markup)
+
+    def test_split_interwiki(self):
+        app.cfg.namespace_mapping = [(u'', 'default_backend'), (u'ns1:', 'default_backend'), (u'ns1:ns2:', 'other_backend')]
+        tests = [('', ('Self', '', '')),
+                 ('OtherWiki:', ('OtherWiki', '', '')),
+                 (':ns1:', ('Self', 'ns1', '')),
+                 (':ns3:foo', ('Self', '', ':ns3:foo')),
+                 ('SomePage', ('Self', '', 'SomePage')),
+                 ('OtherWiki:OtherPage', ('OtherWiki', '', 'OtherPage')),
+                 ('NonExistentWiki:OtherPage', ('Self', '', 'NonExistentWiki:OtherPage')),
+                 (':ns1:OtherPage', ('Self', 'ns1', 'OtherPage')),
+                 (':ns1:ns2:OtherPage', ('Self', 'ns1:ns2', 'OtherPage')),
+                 ('ns1:OtherPage', ('Self', 'ns1', 'OtherPage')),
+                 ('ns1:ns2:OtherPage', ('Self', 'ns1:ns2', 'OtherPage')),
+                 ('OtherWiki:ns1:OtherPage', ('OtherWiki', 'ns1', 'OtherPage')),
+                 ('OtherWiki:ns1:ns2:OtherPage', ('OtherWiki', 'ns1:ns2', 'OtherPage')),
+                 ('OtherWiki:ns3:ns2:OtherPage/foo', ('OtherWiki', '', 'ns3:ns2:OtherPage/foo')),
+                ]
+        for markup, (wikiname, namespace, pagename) in tests:
+            assert split_interwiki(markup) == (wikiname, namespace, pagename)
+            wikiname, namespace, pagename = split_interwiki(markup)
+            assert isinstance(namespace, unicode)
+            assert isinstance(pagename, unicode)
+            assert isinstance(wikiname, unicode)
 
     def testJoinWiki(self):
-        tests = [(('http://example.org/', u'SomePage'), 'http://example.org/SomePage'),
-                 (('http://example.org/?page=$PAGE&action=show', u'SomePage'), 'http://example.org/?page=SomePage&action=show'),
-                 (('http://example.org/', u'Aktuelle\xc4nderungen'), 'http://example.org/Aktuelle%C3%84nderungen'),
-                 (('http://example.org/$PAGE/show', u'Aktuelle\xc4nderungen'), 'http://example.org/Aktuelle%C3%84nderungen/show'),
+        tests = [(('http://example.org/', u'SomePage', ''), 'http://example.org/SomePage'),
+                 (('', u'SomePage', ''), 'SomePage'),
+                 (('http://example.org/?page=$PAGE&action=show', u'SomePage', ''), 'http://example.org/?page=SomePage&action=show'),
+                 (('http://example.org/', u'Aktuelle\xc4nderungen', ''), 'http://example.org/Aktuelle%C3%84nderungen'),
+                 (('http://example.org/$PAGE/show', u'Aktuelle\xc4nderungen', ''), 'http://example.org/Aktuelle%C3%84nderungen/show'),
+
+                 (('http://example.org/', u'SomePage', u'ns1'), 'http://example.org/:ns1:SomePage'),
+                 (('http://example.org/?page=$PAGE&action=show&namespace=$NAMESPACE', u'SomePage', u'ns1'), 'http://example.org/?page=SomePage&action=show&namespace=ns1'),
+                 (('http://example.org/', u'Aktuelle\xc4nderungen', u'ns1ççç'), 'http://example.org/:ns1%C3%83%C2%A7%C3%83%C2%A7%C3%83%C2%A7:Aktuelle%C3%84nderungen'),
+                 (('http://example.org/$NAMESPACE/$PAGE/show', u'Aktuelle\xc4nderungen', u'nsç1'), 'http://example.org/ns%C3%83%C2%A71/Aktuelle%C3%84nderungen/show'),
                 ]
-        for (baseurl, pagename), url in tests:
-            assert join_wiki(baseurl, pagename) == url
-
+        for (baseurl, pagename, namespace), url in tests:
+            assert join_wiki(baseurl, pagename, namespace) == url
 
 class TestInterWikiMapBackend(object):
     """
--- a/MoinMoin/util/interwiki.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/MoinMoin/util/interwiki.py	Sun Feb 10 19:55:14 2013 +0100
@@ -27,7 +27,7 @@
     """
     check if <wiki_name> is THIS wiki
     """
-    return wiki_name in ['', 'Self', app.cfg.interwikiname, ]
+    return wiki_name in [u'', u'Self', app.cfg.interwikiname, ]
 
 
 def is_known_wiki(wiki_name):
@@ -40,7 +40,7 @@
     return wiki_name in app.cfg.interwiki_map
 
 
-def url_for_item(item_name, wiki_name='', rev=CURRENT, endpoint='frontend.show_item', _external=False):
+def url_for_item(item_name, wiki_name=u'', namespace=u'', rev=CURRENT, endpoint=u'frontend.show_item', _external=False):
     """
     Compute URL for some local or remote/interwiki item.
 
@@ -57,6 +57,8 @@
     Computed URLs are always fully specified.
     """
     if is_local_wiki(wiki_name):
+        if namespace:
+            item_name = u':{0}:{1}'.format(namespace, item_name)
         if rev is None or rev == CURRENT:
             url = url_for(endpoint, item_name=item_name, _external=_external)
         else:
@@ -66,46 +68,102 @@
             wiki_base_url = app.cfg.interwiki_map[wiki_name]
         except KeyError, err:
             logging.warning("no interwiki_map entry for {0!r}".format(wiki_name))
-            url = '' # can we find something useful?
+            if namespace:
+                item_name = u'{0}:{1}'.format(namespace, item_name)
+            if wiki_name:
+                url = u'{0}:{1}'.format(wiki_name, item_name)
+            url = u'/{0}'.format(url)
         else:
             if (rev is None or rev == CURRENT) and endpoint == 'frontend.show_item':
                 # we just want to show latest revision (no special revision given) -
                 # this is the generic interwiki url support, should work for any remote wiki
-                url = join_wiki(wiki_base_url, item_name)
+                url = join_wiki(wiki_base_url, item_name, namespace)
             else:
                 # rev and/or endpoint was given, assume same URL building as for local wiki.
                 # we need this for moin wiki farms, e.g. to link from search results to
                 # some specific item/revision in another farm wiki.
+                if namespace:
+                    item_name = u'{0}:{1}'.format(namespace, item_name)
                 local_url = url_for(endpoint, item_name=item_name, rev=rev, _external=False)
                 # we know that everything left of the + belongs to script url, but we
                 # just want e.g. +show/42/FooBar to append it to the other wiki's
                 # base URL.
-                i = local_url.index('/+')
+                i = local_url.index('/%2B')
                 path = local_url[i+1:]
                 url = wiki_base_url + path
     return url
 
+def _split_namespace(namespaces, url):
+    """
+    Find the longest namespace in the set.
+    the namespaces are separated by colons (:).
+    Example:
+        namespaces_set(['ns1', 'ns1:ns2'])
+        url: ns1:urlalasd return: ns1, urlalasd
+        url: ns3:urlalasd return: '', ns3:urlalasd
+        url: ns2:urlalasd return: '', ns2:urlalasd
+        url: ns1:ns2:urlalasd return: ns1:ns2, urlalasd
+    param namespaces_set: set of namespaces (strings) to search
+    param url: string
+    returns: (namespace, url)
+    """
+    namespace = u''
+    tokens_list = url.split(':')
+    for token in tokens_list:
+        if namespace:
+            token = u'{0}:{1}'.format(namespace, token)
+        if token in namespaces:
+            namespace = token
+        else:
+            break
+    if namespace:
+        length = len(namespace) + 1
+        url = url[length:]
+    return namespace, url
 
 def split_interwiki(wikiurl):
     """ Split a interwiki name, into wikiname and pagename, e.g:
 
-    'MoinMoin:FrontPage' -> "MoinMoin", "FrontPage"
-    'FrontPage' -> "Self", "FrontPage"
-    'MoinMoin:Page with blanks' -> "MoinMoin", "Page with blanks"
-    'MoinMoin:' -> "MoinMoin", ""
-
+    'MoinMoin:FrontPage' -> "MoinMoin", "", "FrontPage"
+    'FrontPage' -> "Self", "", "FrontPage"
+    'MoinMoin:Page with blanks' -> "MoinMoin", "", "Page with blanks"
+    'MoinMoin:' -> "MoinMoin", "", ""
+    'MoinMoin:interwikins:AnyPage' -> "MoinMoin", "interwikins", "AnyPage"
+    ':ns:AnyPage' -> "Self", "ns", "AnyPage" if ns namespace exists or "Self", "", ":ns:AnyPage" if not.
+    'ns:AnyPage' -> "Self", "ns", "AnyPage" if ns namespace exists or "Self", "", "ns:AnyPage" if not.
+    ':ns1:ns2:AnyPage' -> "Self", "ns1:ns2", "AnyPage" if ns1:ns2 namespace exists OR
+                         "Self", "ns1", "ns2:AnyPage" if ns1 namespace exists OR
+                         "Self", "", "ns1:ns2:AnyPage" else.
     :param wikiurl: the url to split
     :rtype: tuple
-    :returns: (wikiname, pagename)
+    :returns: (wikiname, namespace, pagename)
     """
-    try:
-        wikiname, pagename = wikiurl.split(":", 1)
-    except ValueError:
-        wikiname, pagename = 'Self', wikiurl
-    return wikiname, pagename
+    if not isinstance(wikiurl, unicode):
+        wikiurl = wikiurl.decode('utf-8')
+    namespace_mapping = set()
+    for namespace, _ in app.cfg.namespace_mapping:
+        namespace_mapping.add(namespace.rstrip(':'))
+    # Base case: no colon in wikiurl
+    if not ':' in wikiurl:
+        return u'Self', u'', wikiurl
+    if not wikiurl.startswith(':'):
+        wikiname, item_name = _split_namespace(set(app.cfg.interwiki_map.keys()), wikiurl)
+        namespace = u''
+        if not wikiname:
+            namespace, item_name = _split_namespace(set(namespace_mapping), wikiurl)
+            wikiname = u'Self'
+        else:
+            if ':' in wikiname:
+                namespace = wikiname.split(':', 1)[1]
+                wikiname = wikiname.split(':', 1)[0]
+        return wikiname, namespace, item_name
+    else:
+        namespace, item_name = _split_namespace(set(namespace_mapping), wikiurl.split(':', 1)[1])
+        if not namespace:
+            item_name = u':{0}'.format(item_name)
+        return u'Self', namespace, item_name
 
-
-def join_wiki(wikiurl, wikitail):
+def join_wiki(wikiurl, wikitail, namespace):
     """
     Add a (url_quoted) page name to an interwiki url.
 
@@ -114,15 +172,23 @@
 
     :param wikiurl: wiki url, maybe including a $PAGE placeholder
     :param wikitail: page name
+    :param namespace: namespace
     :rtype: string
     :returns: generated URL of the page in the other wiki
     """
     wikitail = url_quote(wikitail, charset=config.charset, safe='/')
+    namespace = url_quote(namespace, charset=config.charset, safe='/')
+    if not('$PAGE' in wikiurl or '$NAMESPACE' in wikiurl):
+        if namespace:
+            namespace = u':{0}:'.format(namespace)
+        elif not wikiurl:
+            return wikitail
+        return wikiurl + namespace + wikitail
     if '$PAGE' in wikiurl:
-        return wikiurl.replace('$PAGE', wikitail)
-    else:
-        return wikiurl + wikitail
-
+        wikiurl = wikiurl.replace('$PAGE', wikitail)
+    if '$NAMESPACE' in wikiurl:
+        wikiurl = wikiurl.replace('$NAMESPACE', namespace)
+    return wikiurl
 
 def getInterwikiName(item_name):
     """
Binary file contrib/serialized/items.moin has changed
--- a/docs/admin/backup.rst	Sat Feb 09 19:38:31 2013 +0100
+++ b/docs/admin/backup.rst	Sun Feb 10 19:55:14 2013 +0100
@@ -26,7 +26,7 @@
 To create a dump of all data stored in moinmoin (wiki items, user profiles), run the
 following command::
 
- moin save --file backup.moin
+ moin save --all-backends --file backup.moin
 
 Please note that this file contains sensitive data like user profiles, wiki
 contents, so store your backups in a safe place that no unauthorized
--- a/wikiconfig.py	Sat Feb 09 19:38:31 2013 +0100
+++ b/wikiconfig.py	Sun Feb 10 19:55:14 2013 +0100
@@ -30,18 +30,19 @@
 
     # This provides a simple default setup for your backend configuration.
     # 'stores:fs:...' indicates that you want to use the filesystem backend.
-    namespace_mapping, acl_mapping = create_simple_mapping(
-                            uri='stores:fs:{0}/%(nsname)s/%(kind)s'.format(data_dir),
-                            # XXX we use rather relaxed ACLs for the development wiki:
-                            content_acl=dict(before=u'',
-                                             default=u'All:read,write,create,destroy,admin',
-                                             after=u'',
-                                             hierarchic=False, ),
-                            user_profile_acl=dict(before=u'',
-                                                  default=u'All:read,write,create,destroy,admin',
-                                                  after=u'',
-                                                  hierarchic=False, ),
-                            )
+    # Alternatively you can set up the mapping yourself (see HelpOnStorageConfiguration).
+    namespace_mapping, backend_mapping, acl_mapping = \
+        create_simple_mapping(uri='stores:fs:{0}/%(backend)s/%(kind)s'.format(data_dir),
+                              # XXX we use rather relaxed ACLs for the development wiki:
+                              content_acl=dict(before=u'',
+                                               default=u'All:read,write,create,destroy,admin',
+                                               after=u'',
+                                               hierarchic=False, ),
+                              user_profile_acl=dict(before=u'',
+                                                    default=u'All:read,write,create,destroy,admin',
+                                                    after=u'',
+                                                    hierarchic=False, ),
+                             )
 
     #item_root = u'Home' # front page