Commits

Bruno Martin committed 005e12d

interwiki and namespaces related url generation and split adapted to namespaces. Proper documentation still missing.

  • Participants
  • Parent commits 4010e5e
  • Branches namespaces

Comments (0)

Files changed (3)

MoinMoin/themes/__init__.py

         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
         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

MoinMoin/util/_tests/test_interwiki.py

 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):
     """

MoinMoin/util/interwiki.py

     """
     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):
     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.
 
     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:
             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.
 
 
     :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):
     """
     :rtype: unicode
     :returns: wiki_name:item_name
     """
-    return "{0}:{1}".format(app.cfg.interwikiname, item_name)
+    return u'{0}:{1}'.format(app.cfg.interwikiname, item_name)
 
 
 def getInterwikiHome(username):