Skip to content

Commit

Permalink
invites: Add option to receive direct notification.
Browse files Browse the repository at this point in the history
Previously, when a user's invitations were accepted,
they got notification from welcome bot.
However some user may not want this everytime.

Presently, there is a personal notification setting
which lets you decide if you want notification.

Fixes: #20398.
  • Loading branch information
shashank-23002 committed Feb 19, 2024
1 parent f6b1fe9 commit c0a19c4
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 3 deletions.
5 changes: 5 additions & 0 deletions web/src/invite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function reset_error_messages(): void {
function get_common_invitation_data(): {
csrfmiddlewaretoken: string;
invite_as: number;
send_notification: boolean;
stream_ids: string;
invite_expires_in_minutes: string;
invitee_emails?: string;
Expand All @@ -49,6 +50,7 @@ function get_common_invitation_data(): {
$<HTMLSelectElement & {type: "select-one"}>("select:not([multiple])#invite_as").val()!,
10,
);
const send_notification = $("#receive-invite-acceptance-notification").is(":checked");
const raw_expires_in = $<HTMLSelectElement & {type: "select-one"}>(
"select:not([multiple])#expires_in",
).val()!;
Expand Down Expand Up @@ -77,6 +79,7 @@ function get_common_invitation_data(): {
const data = {
csrfmiddlewaretoken: csrf_token,
invite_as,
send_notification,
stream_ids: JSON.stringify(stream_ids),
invite_expires_in_minutes: JSON.stringify(expires_in),
};
Expand Down Expand Up @@ -374,6 +377,7 @@ function open_invite_user_modal(e: JQuery.ClickEvent<Document, undefined>): void
"loading-text",
$t({defaultMessage: "Inviting..."}),
);
$("#receive-invite-acceptance-notification-container").show();
toggle_invite_submit_button();
reset_error_messages();
});
Expand All @@ -388,6 +392,7 @@ function open_invite_user_modal(e: JQuery.ClickEvent<Document, undefined>): void
$t({defaultMessage: "Generating link..."}),
);
$("#invite-user-modal .dialog_submit_button").prop("disabled", false);
$("#receive-invite-acceptance-notification-container").hide();
reset_error_messages();
});

Expand Down
7 changes: 7 additions & 0 deletions web/templates/invite_user_modal.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
<textarea rows="2" id="invitee_emails" name="invitee_emails" class="invitee_emails" placeholder="{{t 'One or more email addresses...' }}"></textarea>
</div>
</div>
<div class="input-group new-style" id="receive-invite-acceptance-notification-container">
<label class="checkbox display-block">
<input type="checkbox" id="receive-invite-acceptance-notification" checked/>
<span></span>
{{t "Send me a direct message when my invitation is accepted"}}
</label>
</div>
<div class="input-group">
<label for="expires_in">{{t "Invitation expires after" }}</label>
<select id="expires_in" class="invite-expires-in modal_select bootstrap-focus-style">
Expand Down
1 change: 1 addition & 0 deletions zerver/actions/create_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ def process_new_human_user(
and prereg_user is not None
and prereg_user.referred_by is not None
and prereg_user.referred_by.is_active
and prereg_user.send_notification
):
# This is a cross-realm direct message.
with override_language(prereg_user.referred_by.default_language):
Expand Down
7 changes: 6 additions & 1 deletion zerver/actions/invites.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ def do_invite_users(
user_profile: UserProfile,
invitee_emails: Collection[str],
streams: Collection[Stream],
send_notification: bool = True,
*,
invite_expires_in_minutes: Optional[int],
invite_as: int = PreregistrationUser.INVITE_AS["MEMBER"],
Expand Down Expand Up @@ -299,7 +300,11 @@ def do_invite_users(
for email in validated_emails:
# The logged in user is the referrer.
prereg_user = PreregistrationUser(
email=email, referred_by=user_profile, invited_as=invite_as, realm=user_profile.realm
email=email,
referred_by=user_profile,
invited_as=invite_as,
realm=user_profile.realm,
send_notification=send_notification,
)
prereg_user.save()
stream_ids = [stream.id for stream in streams]
Expand Down
18 changes: 18 additions & 0 deletions zerver/migrations/0497_preregistrationuser_send_notification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.9 on 2024-02-17 17:05

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("zerver", "0496_alter_scheduledmessage_read_by_sender"),
]

operations = [
migrations.AddField(
model_name="preregistrationuser",
name="send_notification",
field=models.BooleanField(default=True),
),
]
1 change: 1 addition & 0 deletions zerver/models/prereg_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class PreregistrationUser(models.Model):
full_name = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH, null=True)
full_name_validated = models.BooleanField(default=False)
referred_by = models.ForeignKey(UserProfile, null=True, on_delete=CASCADE)
send_notification = models.BooleanField(default=True)
streams = models.ManyToManyField("zerver.Stream")
invited_at = models.DateTimeField(auto_now=True)
realm_creation = models.BooleanField(default=False)
Expand Down
39 changes: 38 additions & 1 deletion zerver/tests/test_invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def invite(
self,
invitee_emails: str,
stream_names: Sequence[str],
send_notification: bool = True,
invite_expires_in_minutes: Optional[int] = INVITATION_LINK_VALIDITY_MINUTES,
body: str = "",
invite_as: int = PreregistrationUser.INVITE_AS["MEMBER"],
Expand All @@ -195,6 +196,7 @@ def invite(
"/json/invites",
{
"invitee_emails": invitee_emails,
"send_notification": orjson.dumps(send_notification).decode(),
"invite_expires_in_minutes": invite_expires_in,
"stream_ids": orjson.dumps(stream_ids).decode(),
"invite_as": invite_as,
Expand Down Expand Up @@ -970,6 +972,41 @@ def test_invite_user_signup_initial_history(self) -> None:
self.assertTrue(invitee_msg.content.startswith("Hello, and welcome to Zulip!"))
self.assertNotIn("demo organization", invitee_msg.content)

"""
New invite to check if notification-bot message is sent
when send_notification is false.
"""

Message.objects.all().delete()

new_invitee = self.nonreg_email("bob")
self.assert_json_success(self.invite(new_invitee, [private_stream_name, "Denmark"], False))
self.assertTrue(find_key_by_email(new_invitee))

self.submit_reg_form_for_user(new_invitee, "password")
new_invitee_profile = self.nonreg_user("bob")
new_invitee_msg_ids = [
um.message_id for um in UserMessage.objects.filter(user_profile=new_invitee_profile)
]
self.assertTrue(public_msg_id in new_invitee_msg_ids)
self.assertFalse(secret_msg_id in new_invitee_msg_ids)
self.assertFalse(invitee_profile.is_realm_admin)

invitee_msg, signups_stream_msg, secret_msg = Message.objects.all().order_by("-id")[0:3]

self.assertEqual(secret_msg.id, secret_msg_id)

self.assertEqual(signups_stream_msg.sender.email, "notification-bot@zulip.com")
self.assertTrue(
signups_stream_msg.content.startswith(
f"@_**alice_zulip.com|{invitee_profile.id}** joined this organization",
)
)

self.assertEqual(invitee_msg.sender.email, "welcome-bot@zulip.com")
self.assertTrue(invitee_msg.content.startswith("Hello, and welcome to Zulip!"))
self.assertNotIn("demo organization", invitee_msg.content)

def test_multi_user_invite(self) -> None:
"""
Invites multiple users with a variety of delimiters.
Expand Down Expand Up @@ -2176,7 +2213,7 @@ def test_resend_never_expiring_invitation(self) -> None:
self.login("iago")
invitee = "resend@zulip.com"

self.assert_json_success(self.invite(invitee, ["Denmark"], None))
self.assert_json_success(self.invite(invitee, ["Denmark"], True, None))
prereg_user = PreregistrationUser.objects.get(email=invitee)

# Verify and then clear from the outbox the original invite email
Expand Down
4 changes: 3 additions & 1 deletion zerver/views/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_success
from zerver.lib.streams import access_stream_by_id
from zerver.lib.validator import check_int, check_int_in, check_list, check_none_or
from zerver.lib.validator import check_bool, check_int, check_int_in, check_list, check_none_or
from zerver.models import MultiuseInvite, PreregistrationUser, Stream, UserProfile

# Convert INVITATION_LINK_VALIDITY_DAYS into minutes.
Expand Down Expand Up @@ -48,6 +48,7 @@ def invite_users_backend(
request: HttpRequest,
user_profile: UserProfile,
invitee_emails_raw: str = REQ("invitee_emails"),
send_notification: bool = REQ("send_notification", json_validator=check_bool, default=True),
invite_expires_in_minutes: Optional[int] = REQ(
json_validator=check_none_or(check_int), default=INVITATION_LINK_VALIDITY_MINUTES
),
Expand Down Expand Up @@ -97,6 +98,7 @@ def invite_users_backend(
user_profile,
invitee_emails,
streams,
send_notification,
invite_expires_in_minutes=invite_expires_in_minutes,
invite_as=invite_as,
)
Expand Down

0 comments on commit c0a19c4

Please sign in to comment.