Index: roundup/cgi/templating.py =================================================================== --- roundup/cgi/templating.py (revision 83377) +++ roundup/cgi/templating.py (working copy) @@ -1343,18 +1343,18 @@ u = s = match.group('url') if not self.protocol_re.search(s): u = 'http://' + s - if s.endswith('>'): - # catch an escaped ">" at the end of the URL - u = s = s[:-4] - e = '>' - elif s.count('(') != s.count(')'): + end = '' + if '>' in s: + # catch an escaped ">" in the URL + pos = s.find('>') + end = s[pos:] + u = s = s[:pos] + if ')' in s and s.count('(') != s.count(')'): # don't include extraneous ')' in the link pos = s.rfind(')') - e = s[pos:] + end = s[pos:] + end u = s = s[:pos] - else: - e = '' - return replacement % (u, s, e) + return replacement % (u, s, end) def _hyper_repl_email(self, match, replacement): s = match.group('email') Index: test/test_templating.py =================================================================== --- test/test_templating.py (revision 83377) +++ test/test_templating.py (working copy) @@ -146,11 +146,13 @@ def test_url_replace(self): p = StringHTMLProperty(self.client, 'test', '1', None, 'test', '') def t(s): return p.hyper_re.sub(p._hyper_repl, s) - ae = self.assertEquals + ae = self.assertEqual ae(t('http://roundup.net/'), 'http://roundup.net/') ae(t('<HTTP://roundup.net/>'), '<HTTP://roundup.net/>') + ae(t('<http://roundup.net/>.'), + '<http://roundup.net/>.') ae(t('<www.roundup.net>'), '<www.roundup.net>') ae(t('(www.roundup.net)'), @@ -164,6 +166,12 @@ ae(t('(e.g. http://en.wikipedia.org/wiki/Python_(programming_language)).'), '(e.g. ' 'http://en.wikipedia.org/wiki/Python_(programming_language)).') + ae(t('(e.g. http://en.wikipedia.org/wiki/Python_(programming_language))>.'), + '(e.g. ' + 'http://en.wikipedia.org/wiki/Python_(programming_language))>.') + ae(t('(e.g. http://en.wikipedia.org/wiki/Python_(programming_language>)).'), + '(e.g. ' + 'http://en.wikipedia.org/wiki/Python_(programming_language>)).') ''' class HTMLPermissions: