diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index d9955ca448..e68d2fabb3 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -762,8 +762,8 @@ def send_welcome_bot_response(message): "Feel free to continue using this space to practice your new messaging " "skills. Or, try clicking on some of the stream names to your left!") -def render_incoming_message(message, content, user_ids, realm): - # type: (Message, Text, Set[int], Realm) -> Text +def render_incoming_message(message, content, user_ids, realm, mention_data=None): + # type: (Message, Text, Set[int], Realm, Optional[bugdown.MentionData]) -> Text realm_alert_words = alert_words_in_realm(realm) try: rendered_content = render_markdown( @@ -772,6 +772,7 @@ def render_incoming_message(message, content, user_ids, realm): realm=realm, realm_alert_words=realm_alert_words, user_ids=user_ids, + mention_data=mention_data, ) except BugdownRenderingException: raise JsonableError(_('Unable to render message')) @@ -1021,6 +1022,12 @@ def do_send_messages(messages_maybe_none): message['realm'] = message.get('realm', message['message'].sender.realm) for message in messages: + mention_data = bugdown.MentionData( + realm_id=message['realm'].id, + content=message['message'].content, + ) + message['mention_data'] = mention_data + if message['message'].recipient.type == Recipient.STREAM: stream_id = message['message'].recipient.type_id stream_topic = StreamTopicTarget( @@ -1047,11 +1054,14 @@ def do_send_messages(messages_maybe_none): # Render our messages. for message in messages: assert message['message'].rendered_content is None + rendered_content = render_incoming_message( message['message'], message['message'].content, message['active_user_ids'], - message['realm']) + message['realm'], + mention_data=message['mention_data'], + ) message['message'].rendered_content = rendered_content message['message'].rendered_content_version = bugdown_version links_for_embed |= message['message'].links_for_preview diff --git a/zerver/lib/bugdown/__init__.py b/zerver/lib/bugdown/__init__.py index 65118bf64d..16cce05a82 100644 --- a/zerver/lib/bugdown/__init__.py +++ b/zerver/lib/bugdown/__init__.py @@ -1148,7 +1148,7 @@ class UserMentionPattern(markdown.inlinepatterns.Pattern): name = match wildcard = mention.user_mention_matches_wildcard(name) - user = db_data['full_name_info'].get(name.lower(), None) + user = db_data['mention_data'].get_user(name) if wildcard: current_message.mentions_wildcard = True @@ -1549,6 +1549,16 @@ def get_full_name_info(realm_id, full_names): } return dct +class MentionData(object): + def __init__(self, realm_id, content): + # type: (int, Text) -> None + full_names = possible_mentions(content) + self.full_name_info = get_full_name_info(realm_id, full_names) + + def get_user(self, name): + # type: (Text) -> Optional[FullNameInfo] + return self.full_name_info.get(name.lower(), None) + def get_stream_name_info(realm, stream_names): # type: (Realm, Set[Text]) -> Dict[Text, FullNameInfo] if not stream_names: @@ -1575,8 +1585,8 @@ def get_stream_name_info(realm, stream_names): return dct -def do_convert(content, message=None, message_realm=None, possible_words=None, sent_by_bot=False): - # type: (Text, Optional[Message], Optional[Realm], Optional[Set[Text]], Optional[bool]) -> Text +def do_convert(content, message=None, message_realm=None, possible_words=None, sent_by_bot=False, mention_data=None): + # type: (Text, Optional[Message], Optional[Realm], Optional[Set[Text]], Optional[bool], Optional[MentionData]) -> Text """Convert Markdown to HTML, with Zulip-specific settings and hacks.""" # This logic is a bit convoluted, but the overall goal is to support a range of use cases: # * Nothing is passed in other than content -> just run default options (e.g. for docs) @@ -1623,8 +1633,9 @@ def do_convert(content, message=None, message_realm=None, possible_words=None, s # if there is syntax in the message that might use them, since # the fetches are somewhat expensive and these types of syntax # are uncommon enough that it's a useful optimization. - full_names = possible_mentions(content) - full_name_info = get_full_name_info(message_realm.id, full_names) + + if mention_data is None: + mention_data = MentionData(message_realm.id, content) emails = possible_avatar_emails(content) email_info = get_email_info(message_realm.id, emails) @@ -1640,7 +1651,7 @@ def do_convert(content, message=None, message_realm=None, possible_words=None, s db_data = { 'possible_words': possible_words, 'email_info': email_info, - 'full_name_info': full_name_info, + 'mention_data': mention_data, 'realm_emoji': realm_emoji, 'sent_by_bot': sent_by_bot, 'stream_names': stream_name_info, @@ -1694,9 +1705,9 @@ def bugdown_stats_finish(): bugdown_total_requests += 1 bugdown_total_time += (time.time() - bugdown_time_start) -def convert(content, message=None, message_realm=None, possible_words=None, sent_by_bot=False): - # type: (Text, Optional[Message], Optional[Realm], Optional[Set[Text]], Optional[bool]) -> Text +def convert(content, message=None, message_realm=None, possible_words=None, sent_by_bot=False, mention_data=None): + # type: (Text, Optional[Message], Optional[Realm], Optional[Set[Text]], Optional[bool], Optional[MentionData]) -> Text bugdown_stats_start() - ret = do_convert(content, message, message_realm, possible_words, sent_by_bot) + ret = do_convert(content, message, message_realm, possible_words, sent_by_bot, mention_data) bugdown_stats_finish() return ret diff --git a/zerver/lib/message.py b/zerver/lib/message.py index 3f03a99773..ba35828153 100644 --- a/zerver/lib/message.py +++ b/zerver/lib/message.py @@ -461,8 +461,8 @@ def access_message(user_profile, message_id): # stream in your realm, so return the message, user_message pair return (message, user_message) -def render_markdown(message, content, realm=None, realm_alert_words=None, user_ids=None): - # type: (Message, Text, Optional[Realm], Optional[RealmAlertWords], Optional[Set[int]]) -> Text +def render_markdown(message, content, realm=None, realm_alert_words=None, user_ids=None, mention_data=None): + # type: (Message, Text, Optional[Realm], Optional[RealmAlertWords], Optional[Set[int]], Optional[bugdown.MentionData]) -> Text """Return HTML for given markdown. Bugdown may add properties to the message object such as `mentions_user_ids` and `mentions_wildcard`. These are only on this Django object and are not saved in the @@ -497,9 +497,14 @@ def render_markdown(message, content, realm=None, realm_alert_words=None, user_i sent_by_bot = get_user_profile_by_id(message.sender_id).is_bot # DO MAIN WORK HERE -- call bugdown to convert - rendered_content = bugdown.convert(content, message=message, message_realm=realm, - possible_words=possible_words, - sent_by_bot=sent_by_bot) + rendered_content = bugdown.convert( + content, + message=message, + message_realm=realm, + possible_words=possible_words, + sent_by_bot=sent_by_bot, + mention_data=mention_data, + ) if message is not None: message.user_ids_with_alert_words = set()