topic-mention: Add Bugdown implementation as StreamTopicPattern.

This commit is contained in:
Rohitt Vashishtha 2019-06-21 21:01:16 +05:30 committed by Tim Abbott
parent a138d8e95c
commit 0ba332bcc0
2 changed files with 75 additions and 1 deletions

View File

@ -33,7 +33,7 @@ from zerver.lib.camo import get_camo_url
from zerver.lib.emoji import translate_emoticons, emoticon_regex from zerver.lib.emoji import translate_emoticons, emoticon_regex
from zerver.lib.mention import possible_mentions, \ from zerver.lib.mention import possible_mentions, \
possible_user_group_mentions, extract_user_group possible_user_group_mentions, extract_user_group
from zerver.lib.url_encoding import encode_stream from zerver.lib.url_encoding import encode_stream, hash_util_encode
from zerver.lib.thumbnail import user_uploads_or_external from zerver.lib.thumbnail import user_uploads_or_external
from zerver.lib.timeout import timeout, TimeoutExpired from zerver.lib.timeout import timeout, TimeoutExpired
from zerver.lib.cache import cache_with_key, NotFoundInCache from zerver.lib.cache import cache_with_key, NotFoundInCache
@ -113,6 +113,19 @@ STREAM_LINK_REGEX = r"""
def get_compiled_stream_link_regex() -> Pattern: def get_compiled_stream_link_regex() -> Pattern:
return verbose_compile(STREAM_LINK_REGEX) return verbose_compile(STREAM_LINK_REGEX)
STREAM_TOPIC_LINK_REGEX = r"""
(?<![^\s'"\(,:<]) # Start after whitespace or specified chars
\#\*\* # and after hash sign followed by double asterisks
(?P<stream_name>[^\*>]+) # stream name can contain anything except >
> # > acts as separator
(?P<topic_name>[^\*]+) # topic name can contain anything
\*\* # ends by double asterisks
"""
@one_time
def get_compiled_stream_topic_link_regex() -> Pattern:
return verbose_compile(STREAM_TOPIC_LINK_REGEX)
LINK_REGEX = None # type: Pattern LINK_REGEX = None # type: Pattern
def get_web_link_regex() -> str: def get_web_link_regex() -> str:
@ -1684,14 +1697,45 @@ class StreamPattern(CompiledPattern):
# href here and instead having the browser auto-add the # href here and instead having the browser auto-add the
# href when it processes a message with one of these, to # href when it processes a message with one of these, to
# provide more clarity to API clients. # provide more clarity to API clients.
# Also do the same for StreamTopicPattern.
stream_url = encode_stream(stream['id'], name) stream_url = encode_stream(stream['id'], name)
el.set('href', '/#narrow/stream/{stream_url}'.format(stream_url=stream_url)) el.set('href', '/#narrow/stream/{stream_url}'.format(stream_url=stream_url))
el.text = '#{stream_name}'.format(stream_name=name) el.text = '#{stream_name}'.format(stream_name=name)
return el return el
return None return None
class StreamTopicPattern(CompiledPattern):
def find_stream_by_name(self, name: Match[str]) -> Optional[Dict[str, Any]]:
db_data = self.markdown.zulip_db_data
if db_data is None:
return None
stream = db_data['stream_names'].get(name)
return stream
def handleMatch(self, m: Match[str]) -> Optional[Element]:
stream_name = m.group('stream_name')
topic_name = m.group('topic_name')
if self.markdown.zulip_message:
stream = self.find_stream_by_name(stream_name)
if stream is None or topic_name is None:
return None
el = markdown.util.etree.Element('a')
el.set('class', 'stream-topic')
el.set('data-stream-id', str(stream['id']))
stream_url = encode_stream(stream['id'], stream_name)
topic_url = hash_util_encode(topic_name)
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)
return el
return None
def possible_linked_stream_names(content: str) -> Set[str]: def possible_linked_stream_names(content: str) -> Set[str]:
matches = re.findall(STREAM_LINK_REGEX, content, re.VERBOSE) matches = re.findall(STREAM_LINK_REGEX, content, re.VERBOSE)
for match in re.finditer(STREAM_TOPIC_LINK_REGEX, content, re.VERBOSE):
matches.append(match.group('stream_name'))
return set(matches) return set(matches)
class AlertWordsNotificationProcessor(markdown.preprocessors.Preprocessor): class AlertWordsNotificationProcessor(markdown.preprocessors.Preprocessor):
@ -1880,6 +1924,7 @@ class Bugdown(markdown.Markdown):
reg.register(markdown.inlinepatterns.DoubleTagPattern(STRONG_EM_RE, 'strong,em'), 'strong_em', 100) reg.register(markdown.inlinepatterns.DoubleTagPattern(STRONG_EM_RE, 'strong,em'), 'strong_em', 100)
reg.register(UserMentionPattern(mention.find_mentions, self), 'usermention', 95) reg.register(UserMentionPattern(mention.find_mentions, self), 'usermention', 95)
reg.register(Tex(r'\B(?<!\$)\$\$(?P<body>[^\n_$](\\\$|[^$\n])*)\$\$(?!\$)\B'), 'tex', 90) reg.register(Tex(r'\B(?<!\$)\$\$(?P<body>[^\n_$](\\\$|[^$\n])*)\$\$(?!\$)\B'), 'tex', 90)
reg.register(StreamTopicPattern(get_compiled_stream_topic_link_regex(), self), 'topic', 87)
reg.register(StreamPattern(get_compiled_stream_link_regex(), self), 'stream', 85) reg.register(StreamPattern(get_compiled_stream_link_regex(), self), 'stream', 85)
reg.register(Avatar(AVATAR_REGEX, self), 'avatar', 80) reg.register(Avatar(AVATAR_REGEX, self), 'avatar', 80)
reg.register(ModalLink(r'!modal_link\((?P<relative_url>[^)]*), (?P<text>[^)]*)\)'), 'modal_link', 75) reg.register(ModalLink(r'!modal_link\((?P<relative_url>[^)]*), (?P<text>[^)]*)\)'), 'modal_link', 75)

View File

@ -1535,6 +1535,35 @@ class BugdownTest(ZulipTestCase):
render_markdown(msg, content), render_markdown(msg, content),
'<p>#<strong>casesens</strong></p>') '<p>#<strong>casesens</strong></p>')
def test_topic_single(self) -> None:
denmark = get_stream('Denmark', get_realm('zulip'))
sender_user_profile = self.example_user('othello')
msg = Message(sender=sender_user_profile, sending_client=get_client("test"))
content = "#**Denmark>some topic**"
self.assertEqual(
render_markdown(msg, content),
'<p><a class="stream-topic" data-stream-id="{d.id}" href="/#narrow/stream/{d.id}-Denmark/topic/some.20topic">#{d.name} &gt; some topic</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'))
sender_user_profile = self.example_user('othello')
msg = Message(sender=sender_user_profile, sending_client=get_client("test"))
content = "This has two links: #**Denmark>some topic** and #**Scotland>other topic**."
self.assertEqual(
render_markdown(msg, content),
'<p>This has two links: '
'<a class="stream-topic" data-stream-id="{denmark.id}" '
'href="/#narrow/stream/{denmark.id}-{denmark.name}/topic/some.20topic">'
'#{denmark.name} &gt; some topic</a>'
' and '
'<a class="stream-topic" data-stream-id="{scotland.id}" '
'href="/#narrow/stream/{scotland.id}-{scotland.name}/topic/other.20topic">'
'#{scotland.name} &gt; other topic</a>'
'.</p>'.format(denmark=denmark, scotland=scotland))
def test_possible_stream_names(self) -> None: def test_possible_stream_names(self) -> None:
content = '''#**test here** content = '''#**test here**
This mentions #**Denmark** too. This mentions #**Denmark** too.