Repository: zulip/zulip

Issue:
Topic mentions shouldn't be processed by linkifier
Steps to reproduce:
1. Add a linkifier pattern, let's use the example:
``#(?P<id>[0-9]+)`` -> ``https://github.com/zulip/zulip/issues/%(id)s``
2. Create a topic that should get linkified, let's say ``#4871``.
3. Now mention the topic (``#**Denmark>#4871**``). What happens is that the ``#4871`` part of the rendered text links to ``https://github.com/zulip/zulip/issues/4871``, but the correct way is for the whole rendered text to be a simple link to the mentioned topic.

![image](https://user-images.githubusercontent.com/45007152/75631227-fc339280-5be8-11ea-9813-8fe0af63717c.png)

@aero31aero Do you have time to look into this?


Discussion:

Resolving patch:
diff --git a/zerver/lib/bugdown/__init__.py b/zerver/lib/bugdown/__init__.py
--- a/zerver/lib/bugdown/__init__.py
+++ b/zerver/lib/bugdown/__init__.py
@@ -1690,7 +1690,8 @@ def handleMatch(self, m: Match[str]) -> Optional[Element]:
             # Also do the same for StreamTopicPattern.
             stream_url = encode_stream(stream['id'], name)
             el.set('href', '/#narrow/stream/{stream_url}'.format(stream_url=stream_url))
-            el.text = '#{stream_name}'.format(stream_name=name)
+            text = '#{stream_name}'.format(stream_name=name)
+            el.text = markdown.util.AtomicString(text)
             return el
         return None
 
@@ -1718,7 +1719,8 @@ def handleMatch(self, m: Match[str]) -> Optional[Element]:
             link = '/#narrow/stream/{stream_url}/topic/{topic_url}'.format(stream_url=stream_url,
                                                                            topic_url=topic_url)
             el.set('href', link)
-            el.text = '#{stream_name} > {topic_name}'.format(stream_name=stream_name, topic_name=topic_name)
+            text = '#{stream_name} > {topic_name}'.format(stream_name=stream_name, topic_name=topic_name)
+            el.text = markdown.util.AtomicString(text)
             return el
         return None
 


Test patch:
diff --git a/zerver/tests/test_bugdown.py b/zerver/tests/test_bugdown.py
--- a/zerver/tests/test_bugdown.py
+++ b/zerver/tests/test_bugdown.py
@@ -1680,6 +1680,29 @@ def test_topic_single(self) -> None:
                 d=denmark
             ))
 
+    def test_topic_atomic_string(self) -> None:
+        realm = get_realm('zulip')
+        # Create a linkifier.
+        sender_user_profile = self.example_user('othello')
+        url_format_string = r"https://trac.zulip.net/ticket/%(id)s"
+        realm_filter = RealmFilter(realm=realm,
+                                   pattern=r"#(?P<id>[0-9]{2,8})",
+                                   url_format_string=url_format_string)
+        realm_filter.save()
+        self.assertEqual(
+            realm_filter.__str__(),
+            '<RealmFilter(zulip): #(?P<id>[0-9]{2,8})'
+            ' https://trac.zulip.net/ticket/%(id)s>')
+        # Create a topic link that potentially interferes with the pattern.
+        denmark = get_stream('Denmark', realm)
+        msg = Message(sender=sender_user_profile, sending_client=get_client("test"))
+        content = "#**Denmark>#1234**"
+        self.assertEqual(
+            render_markdown(msg, content),
+            '<p><a class="stream-topic" data-stream-id="{d.id}" href="/#narrow/stream/{d.id}-Denmark/topic/.231234">#{d.name} &gt; #1234</a></p>'.format(
+                d=denmark
+            ))
+
     def test_topic_multiple(self) -> None:
         denmark = get_stream('Denmark', get_realm('zulip'))
         scotland = get_stream('Scotland', get_realm('zulip'))
@@ -1725,6 +1748,31 @@ def test_stream_unicode(self) -> None:
                 href=href,
             ))
 
+    def test_stream_atomic_string(self) -> None:
+        realm = get_realm('zulip')
+        # Create a linkifier.
+        sender_user_profile = self.example_user('othello')
+        url_format_string = r"https://trac.zulip.net/ticket/%(id)s"
+        realm_filter = RealmFilter(realm=realm,
+                                   pattern=r"#(?P<id>[0-9]{2,8})",
+                                   url_format_string=url_format_string)
+        realm_filter.save()
+        self.assertEqual(
+            realm_filter.__str__(),
+            '<RealmFilter(zulip): #(?P<id>[0-9]{2,8})'
+            ' https://trac.zulip.net/ticket/%(id)s>')
+        # Create a stream that potentially interferes with the pattern.
+        stream = Stream.objects.create(name=u'Stream #1234', realm=realm)
+        msg = Message(sender=sender_user_profile, sending_client=get_client("test"))
+        content = u"#**Stream #1234**"
+        href = '/#narrow/stream/{stream_id}-Stream-.231234'.format(stream_id=stream.id)
+        self.assertEqual(
+            render_markdown(msg, content),
+            u'<p><a class="stream" data-stream-id="{s.id}" href="{href}">#{s.name}</a></p>'.format(
+                s=stream,
+                href=href,
+            ))
+
     def test_stream_invalid(self) -> None:
         sender_user_profile = self.example_user('othello')
         msg = Message(sender=sender_user_profile, sending_client=get_client("test"))