Repository: zulip/zulip

Issue:
User pills for mentions of deactivated users
<!-- Issue description -->

At present, we do not display mentions of deactivated users as user pills. This looks like a bug -- we should use pills for both active and deactivated users.

However, deactivated users should *not* be shown in mention typeahead (just as now).

Related issues:
- #26833
- #26858

<!-- Link to a message in the chat.zulip.org discussion. Message links will still work even if the topic is renamed or resolved. Link back to this issue from the chat.zulip.org thread. -->

[CZO thread](https://chat.zulip.org/#narrow/stream/9-issues/topic/.40-mentioning.20deactivated.20users/near/1645092)



Discussion:
Hi @alya and @zulipbot kindly assign this task to me .I would love to work on it.
@zulipbot claim
Welcome to Zulip, @henilGondalia! We just sent you an invite to collaborate on this repository at https://github.com/zulip/zulip/invitations. Please accept this invite in order to claim this issue and begin a fun, rewarding experience contributing to Zulip!

Here's some tips to get you off to a good start:

- Join me on the [Zulip developers' server](https://chat.zulip.org), to get help, chat about this issue, and meet the other developers.
- [Unwatch this repository](https://help.github.com/articles/unwatching-repositories/), so that you don't get 100 emails a day.

As you work on this issue, you'll also want to refer to the [Zulip code contribution guide](https://zulip.readthedocs.io/en/latest/contributing/index.html), as well as the rest of the developer documentation on that site.

See you on the other side (that is, the pull request side)!

@henilGondalia You have been unassigned from this issue because you have not made any updates for over 14 days. Please feel free to reclaim the issue if you decide to pick up again. Thanks!

@zulipbot claim

Resolving patch:
diff --git a/zerver/lib/markdown/__init__.py b/zerver/lib/markdown/__init__.py
--- a/zerver/lib/markdown/__init__.py
+++ b/zerver/lib/markdown/__init__.py
@@ -1895,6 +1895,8 @@ def handleMatch(  # type: ignore[override] # https://github.com/python/mypy/issu
                     self.zmd.zulip_rendering_result.mentions_topic_wildcard = True
             elif user is not None:
                 assert isinstance(user, FullNameInfo)
+                if not user.is_active:
+                    silent = True
 
                 if not silent:
                     self.zmd.zulip_rendering_result.mentions_user_ids.add(user.id)
diff --git a/zerver/lib/mention.py b/zerver/lib/mention.py
--- a/zerver/lib/mention.py
+++ b/zerver/lib/mention.py
@@ -27,6 +27,7 @@
 class FullNameInfo:
     id: int
     full_name: str
+    is_active: bool
 
 
 @dataclass
@@ -94,17 +95,20 @@ def get_full_name_info_list(self, user_filters: List[UserFilter]) -> List[FullNa
                 UserProfile.objects.filter(
                     Q(realm_id=self.realm_id) | Q(email__in=settings.CROSS_REALM_BOT_EMAILS),
                 )
-                .filter(is_active=True)
                 .filter(
                     functools.reduce(lambda a, b: a | b, q_list),
                 )
                 .only(
                     "id",
                     "full_name",
+                    "is_active",
                 )
             )
 
-            user_list = [FullNameInfo(id=row.id, full_name=row.full_name) for row in rows]
+            user_list = [
+                FullNameInfo(id=row.id, full_name=row.full_name, is_active=row.is_active)
+                for row in rows
+            ]
 
             # We expect callers who take advantage of our cache to supply both
             # id and full_name in the user mentions in their messages.


Test patch:
diff --git a/zerver/tests/test_markdown.py b/zerver/tests/test_markdown.py
--- a/zerver/tests/test_markdown.py
+++ b/zerver/tests/test_markdown.py
@@ -253,6 +253,7 @@ def make_user(email: str, full_name: str) -> UserProfile:
             FullNameInfo(
                 full_name="Fred Flintstone",
                 id=fred2.id,
+                is_active=True,
             ),
         )
         self.assertEqual(
@@ -260,6 +261,7 @@ def make_user(email: str, full_name: str) -> UserProfile:
             FullNameInfo(
                 full_name="Fred Flintstone",
                 id=fred4.id,
+                is_active=True,
             ),
         )
 
@@ -276,6 +278,7 @@ def test_mention_data(self) -> None:
             FullNameInfo(
                 full_name=hamlet.full_name,
                 id=hamlet.id,
+                is_active=True,
             ),
         )
 
@@ -2110,6 +2113,48 @@ def test_mention_silent(self) -> None:
         )
         self.assertEqual(rendering_result.mentions_user_ids, set())
 
+    def test_mention_deactivated_users(self) -> None:
+        sender_user_profile = self.example_user("othello")
+        user_profile = self.example_user("hamlet")
+        change_user_is_active(user_profile, False)
+        msg = Message(
+            sender=sender_user_profile,
+            sending_client=get_client("test"),
+            realm=sender_user_profile.realm,
+        )
+        user_id = user_profile.id
+
+        content = "@**King Hamlet**"
+        rendering_result = render_markdown(msg, content)
+        self.assertEqual(
+            rendering_result.rendered_content,
+            '<p><span class="user-mention silent" '
+            f'data-user-id="{user_id}">'
+            "King Hamlet</span></p>",
+        )
+        self.assertEqual(rendering_result.mentions_user_ids, set())
+
+    def test_mention_silent_deactivated_users(self) -> None:
+        sender_user_profile = self.example_user("othello")
+        user_profile = self.example_user("hamlet")
+        change_user_is_active(user_profile, False)
+        msg = Message(
+            sender=sender_user_profile,
+            sending_client=get_client("test"),
+            realm=sender_user_profile.realm,
+        )
+        user_id = user_profile.id
+
+        content = "@_**King Hamlet**"
+        rendering_result = render_markdown(msg, content)
+        self.assertEqual(
+            rendering_result.rendered_content,
+            '<p><span class="user-mention silent" '
+            f'data-user-id="{user_id}">'
+            "King Hamlet</span></p>",
+        )
+        self.assertEqual(rendering_result.mentions_user_ids, set())
+
     def test_silent_stream_wildcard_mention(self) -> None:
         user_profile = self.example_user("othello")
         msg = Message(