diff --git a/web/src/rendered_markdown.ts b/web/src/rendered_markdown.ts index 5269e50dc5..24a7b2ee4d 100644 --- a/web/src/rendered_markdown.ts +++ b/web/src/rendered_markdown.ts @@ -212,7 +212,7 @@ export const update_elements = ($content: JQuery): void => { if (user_group_id && $(this).find(".highlight").length === 0) { // Edit the mention to show the current name for the // user group, if its not in search. - set_name_in_mention_element(this, user_group.name); + set_name_in_mention_element(this, user_groups.get_display_group_name(user_group.name)); } }); diff --git a/zerver/lib/markdown/__init__.py b/zerver/lib/markdown/__init__.py index b8a7e61ee0..995bacba9c 100644 --- a/zerver/lib/markdown/__init__.py +++ b/zerver/lib/markdown/__init__.py @@ -51,6 +51,7 @@ from zerver.lib.mention import ( FullNameInfo, MentionBackend, MentionData, + get_user_group_mention_display_name, ) from zerver.lib.outgoing_http import OutgoingSession from zerver.lib.subdomains import is_static_or_current_realm_url @@ -1989,7 +1990,7 @@ class UserGroupMentionPattern(CompiledInlineProcessor): if not silent: self.zmd.zulip_rendering_result.mentions_user_group_ids.add(user_group.id) - name = user_group.name + name = get_user_group_mention_display_name(user_group) user_group_id = str(user_group.id) else: # Don't highlight @-mentions that don't refer to a valid user diff --git a/zerver/lib/mention.py b/zerver/lib/mention.py index 26f2b25e11..4103e0f783 100644 --- a/zerver/lib/mention.py +++ b/zerver/lib/mention.py @@ -5,10 +5,12 @@ from re import Match from django.conf import settings from django.db.models import Q +from django_stubs_ext import StrPromise from zerver.lib.user_groups import get_recursive_group_members from zerver.lib.users import get_inaccessible_user_ids from zerver.models import NamedUserGroup, UserProfile +from zerver.models.groups import SystemGroups from zerver.models.streams import get_linkable_streams BEFORE_MENTION_ALLOWED_REGEX = r"(? str: return f"@_**{user_profile.full_name}|{user_profile.id}**" + + +def get_user_group_mention_display_name(user_group: NamedUserGroup) -> StrPromise | str: + if user_group.is_system_group: + return SystemGroups.GROUP_DISPLAY_NAME_MAP[user_group.name] + + return user_group.name diff --git a/zerver/models/groups.py b/zerver/models/groups.py index 668e3bcb3c..78ecc47cae 100644 --- a/zerver/models/groups.py +++ b/zerver/models/groups.py @@ -1,6 +1,7 @@ from django.db import models from django.db.models import CASCADE from django.utils.timezone import now as timezone_now +from django.utils.translation import gettext_lazy from django_cte import CTEManager from zerver.lib.types import GroupPermissionSetting @@ -17,6 +18,17 @@ class SystemGroups: EVERYONE = "role:everyone" NOBODY = "role:nobody" + GROUP_DISPLAY_NAME_MAP = { + NOBODY: gettext_lazy("Nobody"), + OWNERS: gettext_lazy("Owners"), + ADMINISTRATORS: gettext_lazy("Administrators"), + MODERATORS: gettext_lazy("Moderators"), + FULL_MEMBERS: gettext_lazy("Full members"), + MEMBERS: gettext_lazy("Members"), + EVERYONE: gettext_lazy("Everyone"), + EVERYONE_ON_INTERNET: gettext_lazy("Everyone on the internet"), + } + class UserGroup(models.Model): # type: ignore[django-manager-missing] # django-stubs cannot resolve the custom CTEManager yet https://github.com/typeddjango/django-stubs/issues/1023 objects: CTEManager = CTEManager() diff --git a/zerver/tests/test_markdown.py b/zerver/tests/test_markdown.py index d1cc43a710..562ed7670c 100644 --- a/zerver/tests/test_markdown.py +++ b/zerver/tests/test_markdown.py @@ -2871,6 +2871,7 @@ class MarkdownMentionTest(ZulipTestCase): update_message_and_check_flag("edited", False) update_message_and_check_flag("@*support*", True) update_message_and_check_flag("@_*support*", False) + update_message_and_check_flag("@_*role:administrators*", False) def test_user_group_mention_invalid(self) -> None: sender_user_profile = self.example_user("othello") @@ -2907,6 +2908,17 @@ class MarkdownMentionTest(ZulipTestCase): self.assertEqual(rendering_result.mentions_user_group_ids, set()) + admins_group = NamedUserGroup.objects.get( + name=SystemGroups.ADMINISTRATORS, realm=sender_user_profile.realm, is_system_group=True + ) + content = "Please contact @_*role:administrators*" + rendering_result = render_message_markdown(msg, content) + self.assertEqual( + rendering_result.rendered_content, + "
Please contact " + f'Administrators
', + ) + def test_deactivated_user_group_mention(self) -> None: sender_user_profile = self.example_user("othello") msg = Message( @@ -2950,27 +2962,6 @@ class MarkdownMentionTest(ZulipTestCase): assert_silent_mention("```quote\n@*backend*\n```") assert_silent_mention("```quote\n@_*backend*\n```") - def test_system_user_group_mention(self) -> None: - desdemona = self.example_user("desdemona") - iago = self.example_user("iago") - hamlet = self.example_user("hamlet") - moderators_group = NamedUserGroup.objects.get( - realm=iago.realm, name=SystemGroups.MODERATORS, is_system_group=True - ) - content = "@*role:moderators* @**King Hamlet** test message" - - # Owner cannot mention a system user group. - msg = Message(sender=desdemona, sending_client=get_client("test"), realm=desdemona.realm) - rendering_result = render_message_markdown(msg, content) - self.assertEqual(rendering_result.mentions_user_ids, {hamlet.id}) - self.assertNotIn(moderators_group.id, rendering_result.mentions_user_group_ids) - - # Admin belonging to user group also cannot mention a system user group. - msg = Message(sender=iago, sending_client=get_client("test"), realm=iago.realm) - rendering_result = render_message_markdown(msg, content) - self.assertEqual(rendering_result.mentions_user_ids, {hamlet.id}) - self.assertNotIn(moderators_group.id, rendering_result.mentions_user_group_ids) - class MarkdownStreamMentionTests(ZulipTestCase): def test_stream_single(self) -> None: diff --git a/zerver/tests/test_message_send.py b/zerver/tests/test_message_send.py index 378e861ac5..b9382b1a73 100644 --- a/zerver/tests/test_message_send.py +++ b/zerver/tests/test_message_send.py @@ -2322,6 +2322,25 @@ class StreamMessagesTest(ZulipTestCase): result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id)) self.assert_json_success(result) + # Test mentioning system groups where can_mention_group is + # set to "Nobody" group. + self.assertEqual( + moderators_system_group.can_mention_group.named_user_group.name, SystemGroups.NOBODY + ) + content = "Test mentioning user group @*role:moderators*" + + with self.assertRaisesRegex( + JsonableError, + f"You are not allowed to mention user group '{moderators_system_group.name}'.", + ): + self.send_stream_message(iago, "test_stream", content) + + # silent mentioning system groups is allowed. + content = "Test mentioning user group @_*role:moderators*" + msg_id = self.send_stream_message(iago, "test_stream", content) + result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id)) + self.assert_json_success(result) + def test_stream_message_mirroring(self) -> None: user = self.mit_user("starnine") self.subscribe(user, "Verona")