changeset 2217:431815d4f4a6

Fixed interwiki patch to deal with new composite name.
author Ashutosh Singla <ashu1461@gmail.com>
date Sun, 30 Jun 2013 02:23:16 +0530
parents 7a47e226fc71
children 6a798f444129
files MoinMoin/_tests/test_user.py MoinMoin/constants/keys.py MoinMoin/themes/__init__.py MoinMoin/themes/_tests/test_navi_bar.py MoinMoin/util/_tests/test_interwiki.py MoinMoin/util/interwiki.py
diffstat 6 files changed, 201 insertions(+), 122 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/test_user.py	Sun Jun 30 00:44:47 2013 +0530
+++ b/MoinMoin/_tests/test_user.py	Sun Jun 30 02:23:16 2013 +0530
@@ -185,7 +185,7 @@
         # add quicklink
         theUser.quicklink(u'Test_page_added')
         result_on_addition = theUser.quicklinks
-        expected = [u'MoinTest:Test_page_added']
+        expected = [u'MoinTest/Test_page_added']
         assert result_on_addition == expected
 
         # remove quicklink
@@ -212,7 +212,7 @@
         theUser.add_trail(u'item_added')
         theUser = user.User(name=name, password=password)
         result = theUser.get_trail()
-        expected = [u'MoinTest:item_added']
+        expected = [u'MoinTest/item_added']
         assert result == expected
 
     # Sessions -------------------------------------------------------
--- a/MoinMoin/constants/keys.py	Sun Jun 30 00:44:47 2013 +0530
+++ b/MoinMoin/constants/keys.py	Sun Jun 30 02:23:16 2013 +0530
@@ -113,3 +113,8 @@
 # index names
 LATEST_REVS = 'latest_revs'
 ALL_REVS = 'all_revs'
+
+# Values that FIELD can take in the composite name: [NAMESPACE/][@FIELD/]NAME
+FIELDS = [
+    ITEMID, REVID, TAGS, USERID, ITEMLINKS, ITEMTRANSCLUSIONS,
+]
--- a/MoinMoin/themes/__init__.py	Sun Jun 30 00:44:47 2013 +0530
+++ b/MoinMoin/themes/__init__.py	Sun Jun 30 02:23:16 2013 +0530
@@ -105,9 +105,9 @@
         breadcrumbs = []
         trail = user.get_trail()
         for interwiki_item_name in trail:
-            wiki_name, namespace, item_name = split_interwiki(interwiki_item_name)
+            wiki_name, namespace, field, item_name = split_interwiki(interwiki_item_name)
             err = not is_known_wiki(wiki_name)
-            href = url_for_item(item_name, namespace=namespace, wiki_name=wiki_name)
+            href = url_for_item(item_name, namespace=namespace, wiki_name=wiki_name, field=field)
             if is_local_wiki(wiki_name):
                 exists = self.storage.has_item(item_name)
                 wiki_name = ''  # means "this wiki" for the theme code
@@ -191,10 +191,10 @@
         if target.startswith("wiki:"):
             target = target[5:]
 
-        wiki_name, namespace, item_name = split_interwiki(target)
+        wiki_name, namespace, field, item_name = split_interwiki(target)
         if wiki_name == 'Self':
             wiki_name = ''
-        href = url_for_item(item_name, namespace=namespace, wiki_name=wiki_name)
+        href = url_for_item(item_name, namespace=namespace, wiki_name=wiki_name, field=field)
         if not title:
             title = item_name
         return href, title, wiki_name
--- a/MoinMoin/themes/_tests/test_navi_bar.py	Sun Jun 30 00:44:47 2013 +0530
+++ b/MoinMoin/themes/_tests/test_navi_bar.py	Sun Jun 30 02:23:16 2013 +0530
@@ -26,9 +26,9 @@
             #(navilink, (href, text, interwiki)),
             ('ItemName', ('/ItemName', 'ItemName', '')),
             ('[[ItemName|LinkText]]', ('/ItemName', 'LinkText', '')),
-            ('MoinMoin:ItemName', ('http://moinmo.in/ItemName', 'ItemName', 'MoinMoin')),
-            ('[[MoinMoin:ItemName|LinkText]]', ('http://moinmo.in/ItemName', 'LinkText', 'MoinMoin')),
-            ('[[wiki:MoinMoin:ItemName|LinkText]]', ('http://moinmo.in/ItemName', 'LinkText', 'MoinMoin')),
+            ('MoinMoin/ItemName', ('http://moinmo.in/ItemName', 'ItemName', 'MoinMoin')),
+            ('[[MoinMoin/ItemName|LinkText]]', ('http://moinmo.in/ItemName', 'LinkText', 'MoinMoin')),
+            ('[[wiki:MoinMoin/ItemName|LinkText]]', ('http://moinmo.in/ItemName', 'LinkText', 'MoinMoin')),
             ('http://example.org/', ('http://example.org/', 'http://example.org/', '')),
             ('[[http://example.org/|LinkText]]', ('http://example.org/', 'LinkText', '')),
         ]
--- a/MoinMoin/util/_tests/test_interwiki.py	Sun Jun 30 00:44:47 2013 +0530
+++ b/MoinMoin/util/_tests/test_interwiki.py	Sun Jun 30 02:23:16 2013 +0530
@@ -16,7 +16,7 @@
 import pytest
 from flask import current_app as app
 
-from MoinMoin.util.interwiki import split_interwiki, join_wiki, InterWikiMap, url_for_item, _split_namespace
+from MoinMoin.util.interwiki import split_interwiki, join_wiki, InterWikiMap, url_for_item, _split_namespace, split_fqname
 from MoinMoin._tests import wikiconfig
 from MoinMoin.constants.keys import CURRENT
 from MoinMoin.app import before_wiki
@@ -27,100 +27,120 @@
         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/',
+                         'OtherWiki/ns1': 'http://otherwiki.com/ns1/',
+                         'OtherWiki/ns1/ns2': 'http://otherwiki.com/ns1/ns2/',
         }
 
     def test_url_for_item(self):
         before_wiki()
         revid = 'cdc431e0fc624d6fb8372152dcb66457'
 
-        tests = [(('SomePage', '', '', CURRENT, 'frontend.show_item', False), '/SomePage'),
+        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), '/+modify/SomePage'),
+                 (('SomePage', '', '', '', CURRENT, 'frontend.show_item', True), 'http://localhost:8080/SomePage'),
+                 (('SomePage', '', '', '', CURRENT, 'frontend.modify_item', False), '/+modify/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/+modify/SomePage
                  # (('SomePage', '', '', CURRENT, 'frontend.modify_item', True), 'http://localhost:8080/+modify/SomePage'),
-                 (('SomePage', '', '', revid, 'frontend.show_item', False), '/+show/+{0}/SomePage'.format(revid)),
-                 (('SomePage', '', '', revid, 'frontend.show_item_meta', False), '/+meta/+{0}/SomePage'.format(revid)),
+                 (('SomeRevID', '', 'revid', '', revid, 'frontend.show_item', False), '/+show/+{0}/%40revid/SomeRevID'.format(revid)),
+                 (('SomePage', '', '', '', revid, 'frontend.show_item_meta', False), '/+meta/+{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), '/+modify/:ns1:SomePage'),
-                 (('SomePage', '', 'ns1:ns2', CURRENT, 'frontend.show_item_meta', True), 'http://localhost:8080/+meta/:ns1:ns2:SomePage'),
-                 (('SomePage', '', 'ns1', revid, 'frontend.show_item', False), '/+show/+{0}/:ns1:SomePage'.format(revid)),
-                 (('SomePage', '', 'ns1:ns2', revid, 'frontend.show_item_meta', False), '/+meta/+{0}/:ns1:ns2:SomePage'.format(revid)),
+                 (('SomePage', '', '', 'ns1', CURRENT, 'frontend.show_item', False), '/ns1/SomePage'),
+                 (('SomeTag', '', 'tags', 'ns1', CURRENT, 'frontend.show_item', False), '/ns1/%40tags/SomeTag'),
+                 (('SomePage', '', '', 'ns1/ns2', CURRENT, 'frontend.show_item', True), 'http://localhost:8080/ns1/ns2/SomePage'),
+                 (('SomePage', '', '', 'ns1', CURRENT, 'frontend.modify_item', False), '/+modify/ns1/SomePage'),
+                 (('SomePage', '', '', 'ns1/ns2', CURRENT, 'frontend.show_item_meta', True), 'http://localhost:8080/+meta/ns1/ns2/SomePage'),
+                 (('SomePage', '', '', 'ns1', revid, 'frontend.show_item', False), '/+show/+{0}/ns1/SomePage'.format(revid)),
+                 (('SomePage', '', '', 'ns1/ns2', revid, 'frontend.show_item_meta', False), '/+meta/+{0}/ns1/ns2/SomePage'.format(revid)),
+                 (('SomeRevID', '', 'revid', 'ns1/ns2', CURRENT, 'frontend.show_item_meta', False), '/+meta/ns1/ns2/%40revid/SomeRevID'.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'),
+                 (('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/+show/+{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'),
+                 (('SomePage', 'MoinMoin', '', '', revid, 'frontend.show_item', False), 'http://moinmo.in/+show/+{0}/SomePage'.format(revid)),
+                 (('SomeItemID', 'non-existent', 'itemid', '', CURRENT, 'frontend.show_item', False), '/non-existent/@itemid/SomeItemID'),
+                 (('SomePage', 'non-existent', '', 'ns1', CURRENT, 'frontend.show_item', False), '/non-existent/ns1/SomePage'),
                 ]
 
-        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
+        for (item_name, wiki_name, field, namespace, rev, endpoint, _external), url in tests:
+            assert url_for_item(item_name, wiki_name, field, namespace, rev, endpoint, _external) == url
 
     def test__split_namespace(self):
         map = set()
         map.add(u'ns1')
-        map.add(u'ns1:ns2')
+        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')),
+                 ('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')),
+                 ('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')),
+        app.cfg.namespace_mapping = [(u'', 'default_backend'), (u'ns1/', 'default_backend'), (u'ns1/ns2/', 'other_backend')]
+        tests = [('', ('Self', '', 'name_exact', '')),
+                 ('OtherWiki/', ('OtherWiki', '', 'name_exact', '')),
+                 ('/ns1/', ('Self', 'ns1', 'name_exact', '')),
+                 ('/@itemid/', ('Self', '', 'itemid', '')),
+                 ('/ns3/foo', ('Self', '', 'name_exact', 'ns3/foo')),
+                 ('@tags/SomeTag', ('Self', '', 'tags', 'SomeTag')),
+                 ('OtherWiki/OtherPage', ('OtherWiki', '', 'name_exact', 'OtherPage')),
+                 ('NonExistentWiki/OtherPage', ('Self', '', 'name_exact', 'NonExistentWiki/OtherPage')),
+                 ('OtherWiki/ns1/@invalidID/Page', ('OtherWiki', 'ns1', 'name_exact', '@invalidID/Page')),
+                 ('/ns1/OtherPage', ('Self', 'ns1', 'name_exact', 'OtherPage')),
+                 ('/ns1/ns2/OtherPage', ('Self', 'ns1/ns2', 'name_exact', 'OtherPage')),
+                 ('ns1/OtherPage', ('Self', 'ns1', 'name_exact', 'OtherPage')),
+                 ('ns1/ns2/OtherPage', ('Self', 'ns1/ns2', 'name_exact', 'OtherPage')),
+                 ('OtherWiki/ns1/OtherPage', ('OtherWiki', 'ns1', 'name_exact', 'OtherPage')),
+                 ('OtherWiki/ns1/ns2/OtherPage', ('OtherWiki', 'ns1/ns2', 'name_exact', 'OtherPage')),
+                 ('OtherWiki/ns1/ns2/@userid/SomeUserID', ('OtherWiki', 'ns1/ns2', 'userid', 'SomeUserID')),
+                 ('OtherWiki/ns3/ns2/@Notfield/OtherPage/foo', ('OtherWiki', '', 'name_exact', 'ns3/ns2/@Notfield/OtherPage/foo')),
                 ]
-        for markup, (wikiname, namespace, pagename) in tests:
-            assert split_interwiki(markup) == (wikiname, namespace, pagename)
-            wikiname, namespace, pagename = split_interwiki(markup)
+        for markup, (wikiname, namespace, field, pagename) in tests:
+            assert split_interwiki(markup) == (wikiname, namespace, field, pagename)
+            wikiname, namespace, field, pagename = split_interwiki(markup)
             assert isinstance(namespace, unicode)
             assert isinstance(pagename, unicode)
+            assert isinstance(field, unicode)
             assert isinstance(wikiname, unicode)
 
     def testJoinWiki(self):
-        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'),
+        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\xc4'), 'http://example.org/:ns1%C3%84:Aktuelle%C3%84nderungen'),
-                 (('http://example.org/$NAMESPACE/$PAGE/show', u'Aktuelle\xc4nderungen', u'ns\xc41'), 'http://example.org/ns%C3%841/Aktuelle%C3%84nderungen/show'),
+                 (('http://example.org/', u'SomeItemID', 'itemid', u'ns1'), 'http://example.org/ns1/@itemid/SomeItemID'),
+                 (('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\xc4'), 'http://example.org/ns1%C3%84/Aktuelle%C3%84nderungen'),
+                 (('http://example.org/$NAMESPACE/$PAGE/show', u'Aktuelle\xc4nderungen', '', u'ns\xc41'), 'http://example.org/ns%C3%841/Aktuelle%C3%84nderungen/show'),
+                 (('http://example.org/@$FIELD/$PAGE/show', u'Aktuelle\xc4nderungen', u'itemid', u''), 'http://example.org/@itemid/Aktuelle%C3%84nderungen/show'),
                 ]
-        for (baseurl, pagename, namespace), url in tests:
-            assert join_wiki(baseurl, pagename, namespace) == url
+        for (baseurl, pagename, field, namespace), url in tests:
+            assert join_wiki(baseurl, pagename, field, namespace) == url
+
+    def test_split_fqname(self):
+        app.cfg.namespace_mapping = [(u'', 'default_backend'), (u'ns1/', 'default_backend'), (u'ns1/ns2/', 'other_backend')]
+        tests = [('ns1/ns2/@itemid/SomeItemID', ('ns1/ns2', 'itemid', 'SomeItemID')),
+                 ('ns3/@itemid/SomeItemID', ('', 'name_exact', 'ns3/@itemid/SomeItemID')),
+                 ('Page', ('', 'name_exact', 'Page')),
+                 ('ns1/ns2/@tags/SomeTag', ('ns1/ns2', 'tags', 'SomeTag')),
+                 ('@tags/SomeTag', ('', 'tags', 'SomeTag')),
+                 ('ns1/ns2/@notid', ('ns1/ns2', 'name_exact', '@notid')),
+                 ('ns1/ns2/ns3/Thisisapagename/ns4', ('ns1/ns2', 'name_exact', 'ns3/Thisisapagename/ns4')),
+                ]
+        for url, (namespace, field, pagename) in tests:
+            assert split_fqname(url) == (namespace, field, pagename)
 
 
 class TestInterWikiMapBackend(object):
--- a/MoinMoin/util/interwiki.py	Sun Jun 30 00:44:47 2013 +0530
+++ b/MoinMoin/util/interwiki.py	Sun Jun 30 02:23:16 2013 +0530
@@ -14,8 +14,9 @@
 from flask import url_for
 
 import os.path
+from collections import namedtuple
 
-from MoinMoin.constants.keys import CURRENT
+from MoinMoin.constants.keys import CURRENT, FIELDS, NAME_EXACT, NAMESPACE
 from MoinMoin.constants.contenttypes import CHARSET
 
 from MoinMoin import log
@@ -39,7 +40,19 @@
     return wiki_name in app.cfg.interwiki_map
 
 
-def url_for_item(item_name, wiki_name=u'', namespace=u'', rev=CURRENT, endpoint=u'frontend.show_item', _external=False):
+def get_fqname(item_name, field, namespace):
+    """
+    Compute composite name from item_name, field, namespace
+    composite name == [NAMESPACE/][@FIELD/]NAME
+    """
+    if field and field != NAME_EXACT:
+        item_name = u'@{0}/{1}'.format(field, item_name)
+    if namespace:
+        item_name = u'{0}/{1}'.format(namespace, item_name)
+    return item_name
+
+
+def url_for_item(item_name, wiki_name=u'', field=u'', namespace=u'', rev=CURRENT, endpoint=u'frontend.show_item', _external=False):
     """
     Compute URL for some local or remote/interwiki item.
 
@@ -55,9 +68,10 @@
     URLs are built in the same way as local URLs.
     Computed URLs are always fully specified.
     """
+    if field == NAME_EXACT:
+        field = u''
     if is_local_wiki(wiki_name):
-        if namespace:
-            item_name = u':{0}:{1}'.format(namespace, item_name)
+        item_name = get_fqname(item_name, field, namespace)
         if rev is None or rev == CURRENT:
             url = url_for(endpoint, item_name=item_name, _external=_external)
         else:
@@ -67,10 +81,9 @@
             wiki_base_url = app.cfg.interwiki_map[wiki_name]
         except KeyError, err:
             logging.warning("no interwiki_map entry for {0!r}".format(wiki_name))
-            if namespace:
-                item_name = u'{0}:{1}'.format(namespace, item_name)
+            item_name = get_fqname(item_name, field, namespace)
             if wiki_name:
-                url = u'{0}:{1}'.format(wiki_name, item_name)
+                url = u'{0}/{1}'.format(wiki_name, item_name)
             else:
                 url = item_name
             url = u'/{0}'.format(url)
@@ -78,13 +91,12 @@
             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, namespace)
+                url = join_wiki(wiki_base_url, item_name, field, 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)
+                item_name = get_fqname(item_name, field, namespace)
                 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
@@ -98,22 +110,22 @@
 def _split_namespace(namespaces, url):
     """
     Find the longest namespace in the set.
-    the namespaces are separated by colons (:).
+    the namespaces are separated by  slashes (/).
     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
+        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(':')
+    tokens_list = url.split('/')
     for token in tokens_list:
         if namespace:
-            token = u'{0}:{1}'.format(namespace, token)
+            token = u'{0}/{1}'.format(namespace, token)
         if token in namespaces:
             namespace = token
         else:
@@ -124,50 +136,93 @@
     return namespace, url
 
 
+class CompositeName(namedtuple('CompositeName', 'namespace, field, value')):
+    """
+    namedtuple to hold the compositename
+    """
+    @property
+    def split(self):
+        """
+        returns a dict of field_names/field_values
+        """
+        return {NAMESPACE: self.namespace, u'field': self.field, u'item_name': self.value}
+
+    @property
+    def fullname(self):
+        return get_fqname(self.value, self.field, self.namespace)
+
+    def __unicode__(self):
+        return self.fullname
+
+
+def split_fqname(url):
+    """
+    Split a fully qualified url into namespace, field and pagename
+    url -> [NAMESPACE/][@FIELD/]NAME
+    param: url: the url to split
+    returns: a namedtuple CompositeName(namespace, field, itemname)
+    Example:
+        url: u'ns1/ns2/@itemid/Page' return u'ns1/ns2', u'itemid', u'Page'
+        url: u'@revid/OtherPage' return u'', u'revid', u'OtherPage'
+        url: u'ns1/Page' return u'ns1', u'', u'Page'
+        url: u'ns1/ns2/@notfield' return u'ns1/ns2', u'', u'@notfield'
+    """
+    if not url:
+        return CompositeName(u'', NAME_EXACT, u'')
+    namespaces = {namespace.rstrip('/') for namespace, _ in app.cfg.namespace_mapping}
+    namespace, url = _split_namespace(namespaces, url)
+    field = NAME_EXACT
+    if url.startswith('@'):
+        tokens = url[1:].split('/', 1)
+        if tokens[0] in FIELDS:
+            field = tokens[0]
+            url = tokens[1] if len(tokens) > 1 else u''
+    return CompositeName(namespace, field, 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: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.
+    'MoinMoin/FrontPage' -> "MoinMoin", "", "", "FrontPage"
+    'FrontPage' -> "Self", "", "", "FrontPage"
+    'MoinMoin/Page with blanks' -> "MoinMoin", "", "", "Page with blanks"
+    'MoinMoin/' -> "MoinMoin", "", "", ""
+    'MoinMoin/@Someid/SomeValue' -> "MoinMoin", "", "Someid", "SomeValue" if Someid field exists or "MoinMoin", "", "", "Someid/SomePage" if not
+    'MoinMoin/interwikins/AnyPage' -> "MoinMoin", "interwikins", "", "AnyPage"
+    '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.
+    'MoinMoin/ns/@Somefield/AnyPage' -> "MoinMoin", "ns", "", "@Somefield/AnyPage" if ns namespace exists and field Somefield does not OR
+                                     "MoinMoin", "ns", "Somefield", "AnyPage" if ns namespace and field Somefield exist OR
+                                     "MoinMoin", "", "", "ns/@Somefield/AnyPage" else.
     :param wikiurl: the url to split
     :rtype: tuple
-    :returns: (wikiname, namespace, pagename)
+    :returns: (wikiname, namespace, field, 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 '/' in wikiurl:
+        return u'Self', u'', NAME_EXACT, wikiurl
+    wikiname = field = namespace = u''
+    if not wikiurl.startswith('/'):
+        interwiki_mapping = set()
+        for interwiki_name in app.cfg.interwiki_map:
+            interwiki_mapping.add(interwiki_name.split('/')[0])
+        wikiname, item_name = _split_namespace(interwiki_mapping, wikiurl)
+        if wikiname:
+            wikiurl = wikiurl[len(wikiname)+1:]
+        namespace, field, item_name = split_fqname(wikiurl)
         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
+        return wikiname, namespace, field, 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
+        namespace, field, item_name = split_fqname(wikiurl.split('/', 1)[1])
+        return u'Self', namespace, field, item_name
 
 
-def join_wiki(wikiurl, wikitail, namespace):
+def join_wiki(wikiurl, wikitail, field, namespace):
     """
     Add a (url_quoted) page name to an interwiki url.
 
@@ -181,15 +236,14 @@
     :returns: generated URL of the page in the other wiki
     """
     wikitail = url_quote(wikitail, charset=CHARSET, safe='/')
+    field = url_quote(field, charset=CHARSET, safe='/')
     namespace = url_quote(namespace, charset=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 not('$PAGE' in wikiurl or '$NAMESPACE' in wikiurl or '$FIELD' in wikiurl):
+        return wikiurl + get_fqname(wikitail, field, namespace)
     if '$PAGE' in wikiurl:
         wikiurl = wikiurl.replace('$PAGE', wikitail)
+    if '$FIELD' in wikiurl:
+        wikiurl = wikiurl.replace('$FIELD', field)
     if '$NAMESPACE' in wikiurl:
         wikiurl = wikiurl.replace('$NAMESPACE', namespace)
     return wikiurl
@@ -203,7 +257,7 @@
     :rtype: unicode
     :returns: wiki_name:item_name
     """
-    return u"{0}:{1}".format(app.cfg.interwikiname, item_name)
+    return u"{0}/{1}".format(app.cfg.interwikiname, item_name)
 
 
 def getInterwikiHome(username):