mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	user_groups: Send a message on changing user-groups subscribers.
After this commit a notification message is sent to users if they are added to user_groups by someone else or they are removed from user_groups by someone else. Fixes #23642.
This commit is contained in:
		@@ -126,7 +126,7 @@ def add_emoji_to_message() -> Dict[str, object]:
 | 
			
		||||
 | 
			
		||||
    # The message ID here is hardcoded based on the corresponding value
 | 
			
		||||
    # for the example message IDs we use in zulip.yaml.
 | 
			
		||||
    message_id = 46
 | 
			
		||||
    message_id = 47
 | 
			
		||||
    emoji_name = "octopus"
 | 
			
		||||
    emoji_code = "1f419"
 | 
			
		||||
    reaction_type = "unicode_emoji"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ from django.utils.timezone import now as timezone_now
 | 
			
		||||
 | 
			
		||||
from zerver.actions.realm_settings import do_set_realm_property
 | 
			
		||||
from zerver.actions.user_groups import check_add_user_group, promote_new_full_members
 | 
			
		||||
from zerver.actions.users import do_deactivate_user
 | 
			
		||||
from zerver.lib.mention import silent_mention_syntax_for_user
 | 
			
		||||
from zerver.lib.streams import ensure_stream
 | 
			
		||||
from zerver.lib.test_classes import ZulipTestCase
 | 
			
		||||
from zerver.lib.test_helpers import most_recent_usermessage
 | 
			
		||||
@@ -361,14 +363,27 @@ class UserGroupAPITestCase(UserGroupTestCase):
 | 
			
		||||
        self.assertEqual(UserGroupMembership.objects.filter(user_group=user_group).count(), 1)
 | 
			
		||||
 | 
			
		||||
        othello = self.example_user("othello")
 | 
			
		||||
        add = [othello.id]
 | 
			
		||||
        params = {"add": orjson.dumps(add).decode()}
 | 
			
		||||
        # A bot
 | 
			
		||||
        webhook_bot = self.example_user("webhook_bot")
 | 
			
		||||
        # A deactivated user
 | 
			
		||||
        iago = self.example_user("iago")
 | 
			
		||||
        do_deactivate_user(iago, acting_user=None)
 | 
			
		||||
 | 
			
		||||
        params = {"add": orjson.dumps([othello.id]).decode()}
 | 
			
		||||
        initial_last_message = self.get_last_message()
 | 
			
		||||
        result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
        self.assertEqual(UserGroupMembership.objects.filter(user_group=user_group).count(), 2)
 | 
			
		||||
        members = get_direct_memberships_of_users(user_group, [hamlet, othello])
 | 
			
		||||
        self.assert_length(members, 2)
 | 
			
		||||
 | 
			
		||||
        # A notification message is sent for adding to user group.
 | 
			
		||||
        self.assertNotEqual(self.get_last_message(), initial_last_message)
 | 
			
		||||
        expected_notification = (
 | 
			
		||||
            f"{silent_mention_syntax_for_user(hamlet)} added you to the group @_*support*."
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(self.get_last_message().content, expected_notification)
 | 
			
		||||
 | 
			
		||||
        # Test adding a member already there.
 | 
			
		||||
        result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params)
 | 
			
		||||
        self.assert_json_error(result, f"User {othello.id} is already a member of this group")
 | 
			
		||||
@@ -376,6 +391,24 @@ class UserGroupAPITestCase(UserGroupTestCase):
 | 
			
		||||
        members = get_direct_memberships_of_users(user_group, [hamlet, othello])
 | 
			
		||||
        self.assert_length(members, 2)
 | 
			
		||||
 | 
			
		||||
        # Test user adding itself,bot and deactivated user to user group.
 | 
			
		||||
        desdemona = self.example_user("desdemona")
 | 
			
		||||
        self.login_user(desdemona)
 | 
			
		||||
 | 
			
		||||
        params = {"add": orjson.dumps([desdemona.id, iago.id, webhook_bot.id]).decode()}
 | 
			
		||||
        initial_last_message = self.get_last_message()
 | 
			
		||||
        result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(UserGroupMembership.objects.filter(user_group=user_group).count(), 5)
 | 
			
		||||
        members = get_direct_memberships_of_users(
 | 
			
		||||
            user_group, [hamlet, othello, desdemona, iago, webhook_bot]
 | 
			
		||||
        )
 | 
			
		||||
        self.assert_length(members, 5)
 | 
			
		||||
 | 
			
		||||
        # No notification message is sent for adding to user group.
 | 
			
		||||
        self.assertEqual(self.get_last_message(), initial_last_message)
 | 
			
		||||
 | 
			
		||||
        aaron = self.example_user("aaron")
 | 
			
		||||
 | 
			
		||||
        # For normal testing we again log in with hamlet
 | 
			
		||||
@@ -383,20 +416,44 @@ class UserGroupAPITestCase(UserGroupTestCase):
 | 
			
		||||
        self.login_user(hamlet)
 | 
			
		||||
        # Test remove members
 | 
			
		||||
        params = {"delete": orjson.dumps([othello.id]).decode()}
 | 
			
		||||
        initial_last_message = self.get_last_message()
 | 
			
		||||
        result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
        self.assertEqual(UserGroupMembership.objects.filter(user_group=user_group).count(), 1)
 | 
			
		||||
        members = get_direct_memberships_of_users(user_group, [hamlet, othello, aaron])
 | 
			
		||||
        self.assert_length(members, 1)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(UserGroupMembership.objects.filter(user_group=user_group).count(), 4)
 | 
			
		||||
        members = get_direct_memberships_of_users(
 | 
			
		||||
            user_group, [hamlet, othello, aaron, desdemona, webhook_bot, iago]
 | 
			
		||||
        )
 | 
			
		||||
        self.assert_length(members, 4)
 | 
			
		||||
 | 
			
		||||
        # A notification message is sent for removing from user group.
 | 
			
		||||
        self.assertNotEqual(self.get_last_message(), initial_last_message)
 | 
			
		||||
        expected_notification = (
 | 
			
		||||
            f"{silent_mention_syntax_for_user(hamlet)} removed you from the group @_*support*."
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(self.get_last_message().content, expected_notification)
 | 
			
		||||
 | 
			
		||||
        # Test remove a member that's already removed
 | 
			
		||||
        params = {"delete": orjson.dumps([othello.id]).decode()}
 | 
			
		||||
        result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params)
 | 
			
		||||
        self.assert_json_error(result, f"There is no member '{othello.id}' in this user group")
 | 
			
		||||
 | 
			
		||||
        # Test user remove itself,bot and deactivated user from user group.
 | 
			
		||||
        desdemona = self.example_user("desdemona")
 | 
			
		||||
        self.login_user(desdemona)
 | 
			
		||||
 | 
			
		||||
        params = {"delete": orjson.dumps([desdemona.id, iago.id, webhook_bot.id]).decode()}
 | 
			
		||||
        initial_last_message = self.get_last_message()
 | 
			
		||||
        result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(UserGroupMembership.objects.filter(user_group=user_group).count(), 1)
 | 
			
		||||
        members = get_direct_memberships_of_users(user_group, [hamlet, othello, aaron])
 | 
			
		||||
        members = get_direct_memberships_of_users(user_group, [hamlet, othello, desdemona])
 | 
			
		||||
        self.assert_length(members, 1)
 | 
			
		||||
 | 
			
		||||
        # No notification message is sent for removing from user group.
 | 
			
		||||
        self.assertEqual(self.get_last_message(), initial_last_message)
 | 
			
		||||
 | 
			
		||||
        # Test when nothing is provided
 | 
			
		||||
        result = self.client_post(f"/json/user_groups/{user_group.id}/members", info={})
 | 
			
		||||
        msg = 'Nothing to do. Specify at least one of "add" or "delete".'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
from typing import Optional, Sequence
 | 
			
		||||
from typing import List, Optional, Sequence
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
from django.utils.translation import override as override_language
 | 
			
		||||
 | 
			
		||||
from zerver.actions.message_send import do_send_messages, internal_prep_private_message
 | 
			
		||||
from zerver.actions.user_groups import (
 | 
			
		||||
    add_subgroups_to_user_group,
 | 
			
		||||
    bulk_add_members_to_user_group,
 | 
			
		||||
@@ -15,6 +18,7 @@ from zerver.actions.user_groups import (
 | 
			
		||||
)
 | 
			
		||||
from zerver.decorator import require_member_or_admin, require_user_group_edit_permission
 | 
			
		||||
from zerver.lib.exceptions import JsonableError
 | 
			
		||||
from zerver.lib.mention import MentionBackend, silent_mention_syntax_for_user
 | 
			
		||||
from zerver.lib.request import REQ, has_request_variables
 | 
			
		||||
from zerver.lib.response import json_success
 | 
			
		||||
from zerver.lib.user_groups import (
 | 
			
		||||
@@ -29,7 +33,7 @@ from zerver.lib.user_groups import (
 | 
			
		||||
)
 | 
			
		||||
from zerver.lib.users import access_user_by_id, user_ids_to_users
 | 
			
		||||
from zerver.lib.validator import check_bool, check_int, check_list
 | 
			
		||||
from zerver.models import UserProfile
 | 
			
		||||
from zerver.models import UserGroup, UserProfile, get_system_bot
 | 
			
		||||
from zerver.views.streams import compose_views
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -115,6 +119,55 @@ def update_user_group_backend(
 | 
			
		||||
    return json_success(request, data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def notify_for_user_group_subscription_changes(
 | 
			
		||||
    acting_user: UserProfile,
 | 
			
		||||
    recipient_users: List[UserProfile],
 | 
			
		||||
    user_group: UserGroup,
 | 
			
		||||
    *,
 | 
			
		||||
    send_subscription_message: bool = False,
 | 
			
		||||
    send_unsubscription_message: bool = False,
 | 
			
		||||
) -> None:
 | 
			
		||||
    realm = acting_user.realm
 | 
			
		||||
    mention_backend = MentionBackend(realm.id)
 | 
			
		||||
 | 
			
		||||
    notifications = []
 | 
			
		||||
    notification_bot = get_system_bot(settings.NOTIFICATION_BOT, realm.id)
 | 
			
		||||
    for recipient_user in recipient_users:
 | 
			
		||||
        if recipient_user.id == acting_user.id:
 | 
			
		||||
            # Don't send notification message if you subscribed/unsubscribed yourself.
 | 
			
		||||
            continue
 | 
			
		||||
        if recipient_user.is_bot:
 | 
			
		||||
            # Don't send notification message to bots.
 | 
			
		||||
            continue
 | 
			
		||||
        if not recipient_user.is_active:
 | 
			
		||||
            # Don't send notification message to deactivated users.
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        with override_language(recipient_user.default_language):
 | 
			
		||||
            if send_subscription_message:
 | 
			
		||||
                message = _("{user_full_name} added you to the group {group_name}.").format(
 | 
			
		||||
                    user_full_name=silent_mention_syntax_for_user(acting_user),
 | 
			
		||||
                    group_name=f"@_*{user_group.name}*",
 | 
			
		||||
                )
 | 
			
		||||
            if send_unsubscription_message:
 | 
			
		||||
                message = _("{user_full_name} removed you from the group {group_name}.").format(
 | 
			
		||||
                    user_full_name=silent_mention_syntax_for_user(acting_user),
 | 
			
		||||
                    group_name=f"@_*{user_group.name}*",
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        notifications.append(
 | 
			
		||||
            internal_prep_private_message(
 | 
			
		||||
                sender=notification_bot,
 | 
			
		||||
                recipient_user=recipient_user,
 | 
			
		||||
                content=message,
 | 
			
		||||
                mention_backend=mention_backend,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if len(notifications) > 0:
 | 
			
		||||
        do_send_messages(notifications)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_members_to_group_backend(
 | 
			
		||||
    request: HttpRequest, user_profile: UserProfile, user_group_id: int, members: Sequence[int]
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
@@ -135,6 +188,12 @@ def add_members_to_group_backend(
 | 
			
		||||
 | 
			
		||||
    member_user_ids = [member_user.id for member_user in member_users]
 | 
			
		||||
    bulk_add_members_to_user_group(user_group, member_user_ids, acting_user=user_profile)
 | 
			
		||||
    notify_for_user_group_subscription_changes(
 | 
			
		||||
        acting_user=user_profile,
 | 
			
		||||
        recipient_users=member_users,
 | 
			
		||||
        user_group=user_group,
 | 
			
		||||
        send_subscription_message=True,
 | 
			
		||||
    )
 | 
			
		||||
    return json_success(request)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -153,6 +212,12 @@ def remove_members_from_group_backend(
 | 
			
		||||
 | 
			
		||||
    user_profile_ids = [user.id for user in user_profiles]
 | 
			
		||||
    remove_members_from_user_group(user_group, user_profile_ids, acting_user=user_profile)
 | 
			
		||||
    notify_for_user_group_subscription_changes(
 | 
			
		||||
        acting_user=user_profile,
 | 
			
		||||
        recipient_users=user_profiles,
 | 
			
		||||
        user_group=user_group,
 | 
			
		||||
        send_unsubscription_message=True,
 | 
			
		||||
    )
 | 
			
		||||
    return json_success(request)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user