diff --git a/static/js/invite.js b/static/js/invite.js
index a336c119631028..6dd67a58b352b2 100644
--- a/static/js/invite.js
+++ b/static/js/invite.js
@@ -19,6 +19,7 @@ import * as settings_config from "./settings_config";
import * as stream_data from "./stream_data";
import * as ui from "./ui";
import * as ui_report from "./ui_report";
+import {user_settings} from "./user_settings";
import * as util from "./util";
function reset_error_messages() {
@@ -68,6 +69,15 @@ function submit_invitation_form() {
const invitee_emails_group = invitee_emails.closest(".control-group");
const data = get_common_invitation_data();
data.invitee_emails = $("#invitee_emails").val();
+ const receive_private_message_on_invitee_signup = JSON.stringify(
+ $("#receive_pm").prop("checked"),
+ );
+
+ channel.patch({
+ url: "/json/settings",
+ idempotent: true,
+ data: {receive_private_message_on_invitee_signup},
+ });
channel.post({
url: "/json/invites",
@@ -126,6 +136,7 @@ function submit_invitation_form() {
$("#submit-invitation").text($t({defaultMessage: "Invite"}));
$("#submit-invitation").prop("disabled", false);
$("#invitee_emails").focus();
+ $("#receive_pm").prop("checked", true);
ui.get_scroll_element($("#invite_user_form .modal-body"))[0].scrollTop = 0;
},
});
@@ -211,6 +222,7 @@ export function initialize() {
development_environment: page_params.development_environment,
invite_as_options: settings_config.user_role_values,
expires_in_options: settings_config.expires_in_values,
+ receive_pm_enabled: user_settings.receive_private_message_on_invitee_signup,
});
$(".app").append(rendered);
@@ -237,6 +249,7 @@ export function initialize() {
$("#multiuse_radio_section").show();
$("#invite-method-choice").hide();
$("#invitee_emails").prop("disabled", true);
+ $("#receive_pm").prop("disabled", true);
$("#submit-invitation").text($t({defaultMessage: "Generate invite link"}));
$("#submit-invitation").data("loading-text", $t({defaultMessage: "Generating link..."}));
reset_error_messages();
@@ -244,6 +257,7 @@ export function initialize() {
$("#invite-user").on("change", "#generate_multiuse_invite_radio", () => {
$("#invitee_emails").prop("disabled", false);
+ $("#receive_pm").prop("disabled", false);
$("#submit-invitation").text($t({defaultMessage: "Invite"}));
$("#submit-invitation").data("loading-text", $t({defaultMessage: "Inviting..."}));
$("#multiuse_radio_section").hide();
diff --git a/static/js/user_settings.ts b/static/js/user_settings.ts
index bb6bbff0fcbb4a..c366cd71b55936 100644
--- a/static/js/user_settings.ts
+++ b/static/js/user_settings.ts
@@ -37,6 +37,7 @@ export type UserSettingsType = {
send_stream_typing_notifications: boolean;
send_private_typing_notifications: boolean;
send_read_receipts: boolean;
+ receive_private_message_on_invitee_signup: boolean;
};
export let user_settings = {} as UserSettingsType;
diff --git a/static/templates/invite_user.hbs b/static/templates/invite_user.hbs
index edbd6ddbecff8e..21123911ae8649 100644
--- a/static/templates/invite_user.hbs
+++ b/static/templates/invite_user.hbs
@@ -60,6 +60,13 @@
+
diff --git a/templates/zerver/api/changelog.md b/templates/zerver/api/changelog.md
index 83a7424ee36c29..df9998b1230d8b 100644
--- a/templates/zerver/api/changelog.md
+++ b/templates/zerver/api/changelog.md
@@ -20,6 +20,10 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 5.0
+**Feature level 115**
+
+* [`PATCH /settings`](/api/update-settings), [`POST /register`](/api/register-queue), [`PATCH /settings`](/api/get-server-settings): Added new user notification setting `receive_private_message_on_invitee_signup` to allow user to choose whether or not to get private message when an invitee joins.
+
**Feature level 114**
* [`GET /events`](/api/get-events): Added `rendering_only` field to
diff --git a/version.py b/version.py
index 18816eda0b4af2..71905ba4258abf 100644
--- a/version.py
+++ b/version.py
@@ -33,7 +33,7 @@
# Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md, as well as
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
-API_FEATURE_LEVEL = 114
+API_FEATURE_LEVEL = 115
# Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump
diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py
index c0dfe63e2ae6c0..f8ca06bbe461b8 100644
--- a/zerver/lib/actions.py
+++ b/zerver/lib/actions.py
@@ -511,6 +511,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.referred_by.receive_private_message_on_invitee_signup
):
# This is a cross-realm private message.
with override_language(prereg_user.referred_by.default_language):
@@ -7622,7 +7623,10 @@ 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,
)
prereg_user.save()
stream_ids = [stream.id for stream in streams]
diff --git a/zerver/migrations/0375_add_field_receive_private_message_on_invitee_signup.py b/zerver/migrations/0375_add_field_receive_private_message_on_invitee_signup.py
new file mode 100644
index 00000000000000..c32b01bc92d463
--- /dev/null
+++ b/zerver/migrations/0375_add_field_receive_private_message_on_invitee_signup.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.10 on 2022-01-29 16:05
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('zerver', '0374_backfill_user_delete_realmauditlog'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='realmuserdefault',
+ name='receive_private_message_on_invitee_signup',
+ field=models.BooleanField(default=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='receive_private_message_on_invitee_signup',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/zerver/models.py b/zerver/models.py
index 162df0f6b5e90e..a453541c5f4f70 100644
--- a/zerver/models.py
+++ b/zerver/models.py
@@ -1492,6 +1492,7 @@ class UserBaseSettings(models.Model):
enable_marketing_emails: bool = models.BooleanField(default=True)
realm_name_in_notifications: bool = models.BooleanField(default=False)
presence_enabled: bool = models.BooleanField(default=True)
+ receive_private_message_on_invitee_signup: bool = models.BooleanField(default=True)
# Whether or not the user wants to sync their drafts.
enable_drafts_synchronization = models.BooleanField(default=True)
@@ -1542,7 +1543,10 @@ class UserBaseSettings(models.Model):
)
notification_setting_types = {
- **notification_settings_legacy
+ **notification_settings_legacy,
+ **dict(
+ receive_private_message_on_invitee_signup=bool,
+ ),
} # Add new notifications settings here.
# Define the types of the various automatically managed properties
diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml
index 2b81dc081ba415..fd068f8caca8ab 100644
--- a/zerver/openapi/zulip.yaml
+++ b/zerver/openapi/zulip.yaml
@@ -7898,6 +7898,15 @@ paths:
schema:
type: boolean
example: true
+ - name: receive_private_message_on_invitee_signup
+ in: query
+ description: |
+ Whether or not to receive private message on invitee signup.
+
+ **Changes**: New in Zulip 5.0 (feature level 115).
+ schema:
+ type: boolean
+ example: true
responses:
"200":
description: Success
@@ -9764,6 +9773,12 @@ paths:
read messages.
**Changes**: New in Zulip 5.0 (feature level 105).
+ receive_private_message_on_invitee_signup:
+ type: boolean
+ description: |
+ Whether or not to receive private message on invitee signup.
+
+ **Changes**: New in Zulip 5.0 (feature level 115).
has_zoom_token:
type: boolean
description: |
@@ -11476,6 +11491,12 @@ paths:
read messages.
**Changes**: New in Zulip 5.0 (feature level 105).
+ receive_private_message_on_invitee_signup:
+ type: boolean
+ description: |
+ Whether or not to receive private message on invitee signup.
+
+ **Changes**: New in Zulip 5.0 (feature level 115).
realm_users:
type: array
description: |
@@ -11669,6 +11690,10 @@ paths:
Present if `realm_user` is present in `fetch_event_types`.
The full name of the current user.
+ receive_private_message_on_invitee_signup:
+ type: boolean
+ description: |
+ Whether or not to receive private message on invitee signup.
cross_realm_bots:
type: array
description: |
@@ -12559,6 +12584,15 @@ paths:
schema:
type: boolean
example: true
+ - name: receive_private_message_on_invitee_signup
+ in: query
+ description: |
+ Whether or not to receive private message on invitee signup.
+
+ **Changes**: New in Zulip 5.0 (feature level 115).
+ schema:
+ type: boolean
+ example: true
responses:
"200":
description: Success
diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py
index 2887531af826dc..00d2a96b595860 100644
--- a/zerver/tests/test_signup.py
+++ b/zerver/tests/test_signup.py
@@ -35,6 +35,7 @@
do_change_full_name,
do_change_realm_subdomain,
do_change_user_role,
+ do_change_user_setting,
do_create_default_stream_group,
do_create_multiuse_invite_link,
do_create_realm,
@@ -1619,6 +1620,48 @@ 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)
+ # when receive_private_message_on_invitee_signup is set to False
+ secret_msg_id = self.send_stream_message(
+ self.example_user("hamlet"),
+ private_stream_name,
+ topic_name="Secret topic",
+ content="Secret message",
+ )
+ invitee = self.nonreg_email("bob")
+ self.assert_json_success(self.invite(invitee, [private_stream_name, "Denmark"]))
+ self.assertTrue(find_key_by_email(invitee))
+
+ prereg_user = PreregistrationUser.objects.get(email=invitee)
+ do_change_user_setting(
+ prereg_user.referred_by,
+ "receive_private_message_on_invitee_signup",
+ False,
+ acting_user=prereg_user.referred_by,
+ )
+ self.submit_reg_form_for_user(invitee, "password")
+ invitee_profile = self.nonreg_user("bob")
+ invitee_msg_ids = [
+ um.message_id for um in UserMessage.objects.filter(user_profile=invitee_profile)
+ ]
+ self.assertTrue(public_msg_id in invitee_msg_ids)
+ self.assertFalse(secret_msg_id in 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"@_**bob_zulip.com|{invitee_profile.id}** just signed up",
+ )
+ )
+
+ 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.
diff --git a/zerver/views/realm.py b/zerver/views/realm.py
index 8dbff794b72d6a..62f7224d588ed2 100644
--- a/zerver/views/realm.py
+++ b/zerver/views/realm.py
@@ -402,6 +402,9 @@ def update_realm_user_settings_defaults(
json_validator=check_bool, default=None
),
send_read_receipts: Optional[bool] = REQ(json_validator=check_bool, default=None),
+ receive_private_message_on_invitee_signup: Optional[bool] = REQ(
+ json_validator=check_bool, default=None
+ ),
) -> HttpResponse:
if notification_sound is not None or email_notifications_batching_period_seconds is not None:
check_settings_values(notification_sound, email_notifications_batching_period_seconds)
diff --git a/zerver/views/user_settings.py b/zerver/views/user_settings.py
index db74f15583eafe..dfa12d8e12f57f 100644
--- a/zerver/views/user_settings.py
+++ b/zerver/views/user_settings.py
@@ -204,6 +204,9 @@ def json_change_settings(
),
send_stream_typing_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None),
send_read_receipts: Optional[bool] = REQ(json_validator=check_bool, default=None),
+ receive_private_message_on_invitee_signup: Optional[bool] = REQ(
+ json_validator=check_bool, default=None
+ ),
) -> HttpResponse:
if (
default_language is not None