changeset 828:902c34d95085

quoting for attachment filenames, wikiutil.load_wikimap, split_wiki can parse quoting, join_wiki does url_quote
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 11 Jun 2006 17:48:25 +0200
parents 1faf38d5c3cb
children 77ad89f9f1bf
files MoinMoin/action/MyPages.py MoinMoin/action/fckdialog.py MoinMoin/converter/text_html_text_moin_wiki.py MoinMoin/formatter/__init__.py MoinMoin/formatter/text_docbook.py MoinMoin/formatter/text_html.py MoinMoin/macro/__init__.py MoinMoin/parser/text_moin_wiki.py MoinMoin/wikiutil.py
diffstat 9 files changed, 151 insertions(+), 160 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/action/MyPages.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/action/MyPages.py	Sun Jun 11 17:48:25 2006 +0200
@@ -24,7 +24,7 @@
     userhomewiki = request.cfg.user_homewiki
     if userhomewiki != 'Self' and userhomewiki != request.cfg.interwikiname:
         interwiki = wikiutil.getInterwikiHomePage(request, username=username)
-        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(request, '%s:%s' % interwiki)
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(request, '%s:"%s"' % interwiki)
         wikiurl = wikiutil.mapURL(request, wikiurl)
         homepageurl = wikiutil.join_wiki(wikiurl, wikitail)
         request.http_redirect('%s?action=MyPages' % homepageurl)
--- a/MoinMoin/action/fckdialog.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/action/fckdialog.py	Sun Jun 11 17:48:25 2006 +0200
@@ -230,8 +230,8 @@
         page_list = ""
     
     # list of interwiki names
-    wikiutil.resolve_wiki(request, "Self:FrontPage")
-    interwiki = request.cfg._interwiki_list.keys()
+    interwiki_list = wikiutil.load_wikimap(request)
+    interwiki = interwiki_list.keys()
     interwiki.sort()
     iwpreferred = request.cfg.interwiki_preferred
     if not iwpreferred or iwpreferred and iwpreferred[-1] != None:
--- a/MoinMoin/converter/text_html_text_moin_wiki.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/converter/text_html_text_moin_wiki.py	Sun Jun 11 17:48:25 2006 +0200
@@ -392,6 +392,18 @@
 ]>
 '''
 
+def pagename_from_url(url_frag):
+    """ url is a fragment of an URL we extract the pagename from by URL-unqouting
+        and possible adding quotes around the pagename if we detect blanks in it.
+    """
+    pagename = wikiutil.url_unquote(url_frag)
+    if " " in pagename:
+        if not '"' in pagename:
+            pagename = '"%s"' % pagename
+        elif not "'" in pagename:
+            pagename = "'%s'" % pagename
+    return pagename
+
 class ConvertError(error.FatalError):
     """ Raise when html to wiki conversion fails """
     name = "MoinMoin Convert Error"
@@ -1088,15 +1100,15 @@
                 wikitag, wikiurl, wikitail, err = wikiutil.resolve_wiki(
                     self.request, title + ":")
                 if not err and href.startswith(wikiurl):
-                    pagename = href[len(wikiurl):].lstrip('/')
+                    pagename = pagename_from_url(href[len(wikiurl):].lstrip('/'))
                     interwikiname = "%s:%s" % (wikitag, pagename)
                 else: 
                     raise ConvertError("Invalid InterWiki link: '%s'" % href)
             elif class_ == "badinterwiki" and title:
                 if href == "/": # we used this as replacement for empty href
                     href = ""
-                pagename = href
-                interwikiname = "%s:%s" % (title, href)
+                pagename = pagename_from_url(href)
+                interwikiname = "%s:%s" % (title, pagename)
             if interwikiname and pagename == text: 
                 self.text.append("%s" % interwikiname)
                 return
--- a/MoinMoin/formatter/__init__.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/formatter/__init__.py	Sun Jun 11 17:48:25 2006 +0200
@@ -94,7 +94,7 @@
             IMPORTANT: on and off must be called with same parameters, see
                        also the text_html formatter.
         """
-        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:"%s"' % (interwiki, pagename))
         if wikitag == 'Self' or wikitag == self.request.cfg.interwikiname:
             if wikitail.find('#') > -1:
                 wikitail, kw['anchor'] = wikitail.split('#', 1)
--- a/MoinMoin/formatter/text_docbook.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/formatter/text_docbook.py	Sun Jun 11 17:48:25 2006 +0200
@@ -367,7 +367,7 @@
         if not on:
             return self.url(on,kw)
 
-        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:"%s"' % (interwiki, pagename))
         wikiurl = wikiutil.mapURL(self.request, wikiurl)
         href = wikiutil.join_wiki(wikiurl, wikitail)
 
--- a/MoinMoin/formatter/text_html.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/formatter/text_html.py	Sun Jun 11 17:48:25 2006 +0200
@@ -502,7 +502,7 @@
         """
         @keyword title: override using the interwiki wikiname as title
         """
-        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:"%s"' % (interwiki, pagename))
         wikiurl = wikiutil.mapURL(self.request, wikiurl)
         if wikitag == 'Self': # for own wiki, do simple links
             if on:
@@ -626,6 +626,7 @@
     def attachment_link(self, url, text, **kw):
         _ = self.request.getText
         pagename, filename = AttachFile.absoluteName(url, self.page.page_name)
+        #self.request.log("attachment_link: url %s pagename %s filename %s" % (url, pagename, filename))
         fname = wikiutil.taintfilename(filename)
         fpath = AttachFile.getFilename(self.request, pagename, fname)
         if not os.path.exists(fpath):
@@ -694,8 +695,7 @@
 
         # check for map file
         if os.path.exists(mappath):
-            # we have a image map. inline it and add a map ref
-            # to the img tag
+            # we have a image map. inline it and add a map ref to the img tag
             try:
                 map = file(mappath, 'r').read()
             except IOError:
--- a/MoinMoin/macro/__init__.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/macro/__init__.py	Sun Jun 11 17:48:25 2006 +0200
@@ -335,13 +335,10 @@
         
     def _macro_InterWiki(self, args):
         from StringIO import StringIO
-
-        # load interwiki list
-        dummy = wikiutil.resolve_wiki(self.request, '')
-
+        interwiki_list = wikiutil.load_wikimap(self.request)
         buf = StringIO()
         buf.write('<dl>')
-        list = self.cfg._interwiki_list.items() # this is where we cached it
+        list = interwiki_list.items() # this is where we cached it
         list.sort()
         for tag, url in list:
             buf.write('<dt><tt><a href="%s">%s</a></tt></dt>' % (
@@ -351,7 +348,6 @@
             else:
                 buf.write('<dd><tt>%s</tt></dd>' % url)
         buf.write('</dl>')
-
         return self.formatter.rawHTML(buf.getvalue())
 
     def _macro_PageCount(self, args):
--- a/MoinMoin/parser/text_moin_wiki.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/parser/text_moin_wiki.py	Sun Jun 11 17:48:25 2006 +0200
@@ -30,8 +30,12 @@
 
     # some common strings
     PARENT_PREFIX = wikiutil.PARENT_PREFIX
+    sq_string = ur"('.*?')" # single quoted string
+    dq_string = ur"(\".*?\")" # double quoted string
+    q_string = ur"(%s|%s)" % (sq_string, dq_string) # quoted string
     attachment_schemas = ["attachment", "inline", "drawing"]
     punct_pattern = re.escape(u'''"\'}]|:,.)?!''')
+    punct_no_quote_pattern = re.escape(u'''\}]|:,.)?!''')
     url_pattern = (u'http|https|ftp|nntp|news|mailto|telnet|wiki|file|irc|' +
             u'|'.join(attachment_schemas) + 
             (config.url_schemas and u'|' + u'|'.join(config.url_schemas) or ''))
@@ -43,17 +47,16 @@
         'subpages': wikiutil.CHILD_PREFIX + '?',
         'parent': ur'(?:%s)?' % re.escape(PARENT_PREFIX),
     }
-    url_rule = ur'%(url_guard)s(%(url)s)\:([^\s\<%(punct)s]|([%(punct)s][^\s\<%(punct)s]))+' % {
+    url_rule = ur'%(url_guard)s(%(url)s)\:(([^\s\<%(punct)s]|([%(punctnq)s][^\s\<%(punct)s]))+|%(q_string)s)' % {
         'url_guard': u'(^|(?<!\w))',
         'url': url_pattern,
         'punct': punct_pattern,
+        'punctnq': punct_no_quote_pattern,
+        'q_string': q_string,
     }
 
     ol_rule = ur"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
     dl_rule = ur"^\s+.*?::\s"
-    sq_string = ur"('.*?')" # single quoted string
-    dq_string = ur"(\".*?\")" # double quoted string
-    q_string = ur"(%s|%s)" % (sq_string, dq_string) # quoted string
 
     # the big, fat, ugly one ;)
     formatting_rules = ur"""(?P<ent_numeric>&#(\d{1,5}|x[0-9a-fA-F]+);)
@@ -158,81 +161,46 @@
         #result.append("<!-- close item end -->\n")
 
 
-    def interwiki(self, url_and_text, **kw):
+    def interwiki(self, target_and_text, **kw):
         # TODO: maybe support [wiki:Page http://wherever/image.png] ?
-        if len(url_and_text) == 1:
-            url = url_and_text[0]
-            text = None
-        else:
-            url, text = url_and_text
+        scheme, rest = target_and_text.split(':', 1)
+        wikiname, pagename, text = wikiutil.split_wiki(rest)
+        if not text:
+            text = pagename
+        #self.request.log("interwiki: split_wiki -> %s.%s.%s" % (wikiname,pagename,text))
 
-        # keep track of whether this is a self-reference, so links
-        # are always shown even the page doesn't exist.
-        is_self_reference = 0
-        url2 = url.lower()
-        if url2.startswith('wiki:self:'):
-            url = url[10:] # remove "wiki:self:"
-            is_self_reference = 1
-        elif url2.startswith('wiki:'):
-            url = url[5:] # remove "wiki:"
-           
-        tag, tail = wikiutil.split_wiki(url)
-        if text is None:
-            if tag:
-                text = tail
-            else:
-                text = url
-                url = ""
-        elif (url.startswith(wikiutil.CHILD_PREFIX) or # fancy link to subpage [wiki:/SubPage text]
-              is_self_reference or # [wiki:Self:LocalPage text] or [:LocalPage:text]
-              Page(self.request, url).exists()): # fancy link to local page [wiki:LocalPage text]
-            return self._word_repl(url, text)
-
-        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, url)
-        href = wikiutil.join_wiki(wikiurl, wikitail)
+        if wikiname.lower() == 'self': # [wiki:Self:LocalPage text] or [:LocalPage:text]
+            return self._word_repl(pagename, text)
 
         # check for image URL, and possibly return IMG tag
-        if not kw.get('pretty_url', 0) and wikiutil.isPicture(wikitail):
+        if not kw.get('pretty_url', 0) and wikiutil.isPicture(pagename):
+            dummy, wikiurl, dummy, wikitag_bad = wikiutil.resolve_wiki(self.request, rest)
+            href = wikiutil.join_wiki(wikiurl, pagename)
+            #self.request.log("interwiki: join_wiki -> %s.%s.%s" % (wikiurl,pagename,href))
             return self.formatter.image(src=href)
 
-        # link to self?
-        if wikitag is None:
-            return self._word_repl(wikitail)
-              
-        return (self.formatter.interwikilink(1, tag, tail) + 
+        return (self.formatter.interwikilink(1, wikiname, pagename) + 
                 self.formatter.text(text) +
-                self.formatter.interwikilink(0, tag, tail))
+                self.formatter.interwikilink(0, wikiname, pagename))
 
-    def attachment(self, url_and_text, **kw):
-        """ This gets called on attachment URLs.
-        """
+    def attachment(self, target_and_text, **kw):
+        """ This gets called on attachment URLs """
         _ = self._
-        if len(url_and_text) == 1:
-            url = url_and_text[0]
-            text = None
-        else:
-            url, text = url_and_text
+        #self.request.log("attachment: target_and_text %s" % target_and_text)
+        scheme, fname, text = wikiutil.split_wiki(target_and_text)
 
-        inline = url.starswith('inline')
-        drawing = url.startswith('drawing')
-        url = url.split(":", 1)[1]
-        url = wikiutil.url_unquote(url, want_unicode=True)
-        text = text or url
+        if scheme == 'drawing':
+            return self.formatter.attachment_drawing(fname, text)
 
-        from MoinMoin.action import AttachFile
-        if drawing:
-            return self.formatter.attachment_drawing(url, text)
-
-        # check for image URL, and possibly return IMG tag
-        # (images are always inlined, just like for other URLs)
-        if not kw.get('pretty_url', 0) and wikiutil.isPicture(url):
-            return self.formatter.attachment_image(url)
+        # check for image, and possibly return IMG tag (images are always inlined)
+        if not kw.get('pretty_url', 0) and wikiutil.isPicture(fname):
+            return self.formatter.attachment_image(fname)
                 
         # inline the attachment
-        if inline:
-            return self.formatter.attachment_inlined(url, text)
+        if scheme == 'inline':
+            return self.formatter.attachment_inlined(fname, text)
 
-        return self.formatter.attachment_link(url, text)
+        return self.formatter.attachment_link(fname, text)
 
     def _u_repl(self, word):
         """Handle underline."""
@@ -369,16 +337,17 @@
         if wikitag_bad:
             return self.formatter.text(word)
         else:
-            return self.interwiki(["wiki:" + word])
+            return self.interwiki("wiki:" + word)
 
     def _url_repl(self, word):
         """Handle literal URLs including inline images."""
         scheme = word.split(":", 1)[0]
 
         if scheme == "wiki":
-            return self.interwiki([word])
+            return self.interwiki(word)
+
         if scheme in self.attachment_schemas:
-            return self.attachment([word])
+            return self.attachment(word)
 
         if wikiutil.isPicture(word):
             word = wikiutil.mapURL(self.request, word)
@@ -415,31 +384,34 @@
         """Handle bracketed URLs."""
         word = word[1:-1] # strip brackets
         
-        # Local extended link?
+        # Local extended link? [:page name:link text] XXX DEPRECATED
         if word[0] == ':':
             words = word[1:].split(':', 1)
             if len(words) == 1:
                 words = words * 2
-            words[0] = 'wiki:Self:%s' % words[0]
-            return self.interwiki(words, pretty_url=1)
-            #return self._word_repl(words[0], words[1])
-
-        # Traditional split on space
-        words = word.split(None, 1)
-        if len(words) == 1:
-            words = words * 2
+            target_and_text = 'wiki:Self:"%s" %s' % tuple(words)
+            return self.interwiki(target_and_text, pretty_url=1)
 
-        if words[0][0] == '#':
-            # anchor link
-            return (self.formatter.url(1, words[0]) +
-                    self.formatter.text(words[1]) +
-                    self.formatter.url(0))
+        scheme_and_rest = word.split(":", 1)
+        if len(scheme_and_rest) == 1: # no scheme
+            # Traditional split on space
+            words = word.split(None, 1)
+            if len(words) == 1:
+                words = words * 2
 
-        scheme = words[0].split(":", 1)[0]
+            if words[0][0] == '#':
+                # anchor link
+                return (self.formatter.url(1, words[0]) +
+                        self.formatter.text(words[1]) +
+                        self.formatter.url(0))
+            raise "what is triggering this?"
+        else:
+            scheme, rest = scheme_and_rest
+
         if scheme == "wiki":
-            return self.interwiki(words, pretty_url=1)
+            return self.interwiki(word, pretty_url=1)
         if scheme in self.attachment_schemas:
-            return self.attachment(words, pretty_url=1)
+            return self.attachment(word, pretty_url=1)
 
         if wikiutil.isPicture(words[1]) and re.match(self.url_rule, words[1]):
             return (self.formatter.url(1, words[0], css='external', do_escape=0) +
--- a/MoinMoin/wikiutil.py	Sun Jun 11 02:19:09 2006 +0200
+++ b/MoinMoin/wikiutil.py	Sun Jun 11 17:48:25 2006 +0200
@@ -478,57 +478,8 @@
 #############################################################################
 ### InterWiki
 #############################################################################
-def strip_quotes(text):
-    """ strip ' or " quotes """
-    if text[0] in "'\"" and text[0] == text[-1]:
-        text = text[1:-1]
-    return text
-
-def split_wiki(wikiurl):
-    """
-    Split a wiki url.
-    
-    @param wikiurl: the url to split
-    @rtype: tuple
-    @return: (tag, tail)
-    """
-    # !!! use a regex here!
-    try:
-        wikitag, tail = wikiurl.split(":", 1) # e.g. MoinMoin:FrontPage
-    except ValueError:
-        try:
-            wikitag, tail = wikiurl.split("/", 1) # for what is this used?
-        except ValueError:
-            wikitag, tail = 'Self', wikiurl
-    tail = strip_quotes(tail)
-    return wikitag, tail
-
-
-def join_wiki(wikiurl, wikitail):
-    """
-    Add a page name to an interwiki url.
-    
-    @param wikiurl: wiki url, maybe including a $PAGE placeholder
-    @param wikitail: page name
-    @rtype: string
-    @return: generated URL of the page in the other wiki
-    """
-    if wikiurl.find('$PAGE') == -1:
-        return wikiurl + wikitail
-    else:
-        return wikiurl.replace('$PAGE', wikitail)
-
-
-def resolve_wiki(request, wikiurl):
-    """
-    Resolve an interwiki link.
-    
-    @param request: the request object
-    @param wikiurl: the InterWiki:PageName link
-    @rtype: tuple
-    @return: (wikitag, wikiurl, wikitail, err)
-    """
-    # load map (once, and only on demand)
+def load_wikimap(request):
+    """ load interwiki map (once, and only on demand) """
     try:
         _interwiki_list = request.cfg._interwiki_list
     except AttributeError:
@@ -568,15 +519,75 @@
 
         # save for later
         request.cfg._interwiki_list = _interwiki_list
-
-    # split wiki url
-    wikitag, tail = split_wiki(wikiurl)
+    
+    return _interwiki_list
+    
+def split_wiki(wikiurl):
+    """ Split a wiki url, e.g:
+    
+    'MoinMoin:FrontPage' -> "MoinMoin", "FrontPage", ""
+    'FrontPage' -> "Self", "FrontPage", ""
+    'MoinMoin:"Page with blanks" link title' -> "MoinMoin", "Page with blanks", "link title"
 
-    # return resolved url
-    if _interwiki_list.has_key(wikitag):
-        return (wikitag, _interwiki_list[wikitag], tail, False)
+    can also be used for:
+
+    'attachment:"filename with blanks.txt" other title' -> "attachment", "filename with blanks.txt", "other title"
+
+    @param wikiurl: the url to split
+    @rtype: tuple
+    @return: (wikiname, pagename, linktext)
+    """
+    try:
+        wikiname, rest = wikiurl.split(":", 1) # e.g. MoinMoin:FrontPage
+    except ValueError:
+        try:
+            wikiname, rest = wikiurl.split("/", 1) # for what is this used?
+        except ValueError:
+            wikiname, rest = 'Self', wikiurl
+    first_char = rest[0]
+    if first_char in "'\"": # quoted pagename
+        pagename_linktext = rest[1:].split(first_char, 1)
+    else: # not quoted, split on whitespace
+        pagename_linktext = rest.split(None, 1)
+    if len(pagename_linktext) == 1:
+        pagename, linktext = pagename_linktext[0], ""
     else:
-        return (wikitag, request.getScriptname(), "/InterWiki", True)
+        pagename, linktext = pagename_linktext
+    linktext = linktext.strip()
+    return wikiname, pagename, linktext
+
+def resolve_wiki(request, wikiurl):
+    """ Resolve an interwiki link.
+    
+    @param request: the request object
+    @param wikiurl: the InterWiki:PageName link
+    @rtype: tuple
+    @return: (wikitag, wikiurl, wikitail, err)
+    """
+    _interwiki_list = load_wikimap(request)
+    wikiname, pagename, linktext = split_wiki(wikiurl)
+    if _interwiki_list.has_key(wikiname):
+        return (wikiname, _interwiki_list[wikiname], pagename, False)
+    else:
+        return (wikiname, request.getScriptname(), "/InterWiki", True)
+
+def join_wiki(wikiurl, wikitail):
+    """
+    Add a (url_quoted) page name to an interwiki url.
+   
+    Note: We can't know what kind of URL quoting a remote wiki expects.
+          We just use a utf-8 encoded string with standard URL quoting.
+          
+    @param wikiurl: wiki url, maybe including a $PAGE placeholder
+    @param wikitail: page name
+    @rtype: string
+    @return: generated URL of the page in the other wiki
+    """
+    wikitail = url_quote(wikitail)
+    if '$PAGE' in wikiurl:
+        return wikiurl.replace('$PAGE', wikitail)
+    else:
+        return wikiurl + wikitail
 
 
 #############################################################################