mirror of
https://github.com/zulip/zulip.git
synced 2025-11-06 15:03:34 +00:00
backend: Allow Administrators to invite new users as admins.
Tweaked by tabbott to have the field before the invitation is completed be called invite_as_admins, not invited_as_admins, for readability. Fixes #6834.
This commit is contained in:
@@ -18,6 +18,17 @@
|
|||||||
<div class="controls">
|
<div class="controls">
|
||||||
<textarea rows="2" id="custom_invite_body" name="custom_body" placeholder="{{ _('Custom message') }}"></textarea>
|
<textarea rows="2" id="custom_invite_body" name="custom_body" placeholder="{{ _('Custom message') }}"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
{% if is_admin %}
|
||||||
|
<label class="control-label" id="invite_as_admin" for"invite_as_admin">{{ _('User(s) join as') }}</label>
|
||||||
|
<div class="controls">
|
||||||
|
<label class="radio">
|
||||||
|
<input type="radio" name="invite_as_admin" value="false" checked/>{{ _('Regular users') }}
|
||||||
|
</label>
|
||||||
|
<label class="radio">
|
||||||
|
<input type="radio" name="invite_as_admin" value="true"/>{{ _('Organization administrators') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="alert" id="invite_status"></div>
|
<div class="alert" id="invite_status"></div>
|
||||||
{% if development_environment %}
|
{% if development_environment %}
|
||||||
|
|||||||
@@ -3754,8 +3754,8 @@ class InvitationError(JsonableError):
|
|||||||
self.errors = errors # type: List[Tuple[Text, str]]
|
self.errors = errors # type: List[Tuple[Text, str]]
|
||||||
self.sent_invitations = sent_invitations # type: bool
|
self.sent_invitations = sent_invitations # type: bool
|
||||||
|
|
||||||
def do_invite_users(user_profile, invitee_emails, streams, body=None):
|
def do_invite_users(user_profile, invitee_emails, streams, invite_as_admin=False, body=None):
|
||||||
# type: (UserProfile, SizedTextIterable, Iterable[Stream], Optional[str]) -> None
|
# type: (UserProfile, SizedTextIterable, Iterable[Stream], Optional[bool], Optional[str]) -> None
|
||||||
validated_emails = [] # type: List[Text]
|
validated_emails = [] # type: List[Text]
|
||||||
errors = [] # type: List[Tuple[Text, str]]
|
errors = [] # type: List[Tuple[Text, str]]
|
||||||
skipped = [] # type: List[Tuple[Text, str]]
|
skipped = [] # type: List[Tuple[Text, str]]
|
||||||
@@ -3787,7 +3787,8 @@ def do_invite_users(user_profile, invitee_emails, streams, body=None):
|
|||||||
# the PreregistrationUser objects and trigger the email invitations.
|
# the PreregistrationUser objects and trigger the email invitations.
|
||||||
for email in validated_emails:
|
for email in validated_emails:
|
||||||
# The logged in user is the referrer.
|
# The logged in user is the referrer.
|
||||||
prereg_user = PreregistrationUser(email=email, referred_by=user_profile)
|
prereg_user = PreregistrationUser(email=email, referred_by=user_profile,
|
||||||
|
invited_as_admin=invite_as_admin)
|
||||||
|
|
||||||
prereg_user.save()
|
prereg_user.save()
|
||||||
stream_ids = [stream.id for stream in streams]
|
stream_ids = [stream.id for stream in streams]
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.5 on 2017-10-19 21:42
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('zerver', '0113_default_stream_group'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='preregistrationuser',
|
||||||
|
name='invited_as_admin',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -794,6 +794,8 @@ class PreregistrationUser(models.Model):
|
|||||||
|
|
||||||
realm = models.ForeignKey(Realm, null=True, on_delete=CASCADE) # type: Optional[Realm]
|
realm = models.ForeignKey(Realm, null=True, on_delete=CASCADE) # type: Optional[Realm]
|
||||||
|
|
||||||
|
invited_as_admin = models.BooleanField(default=False) # type: Optional[bool]
|
||||||
|
|
||||||
class MultiuseInvite(models.Model):
|
class MultiuseInvite(models.Model):
|
||||||
referred_by = models.ForeignKey(UserProfile, on_delete=CASCADE) # Optional[UserProfile]
|
referred_by = models.ForeignKey(UserProfile, on_delete=CASCADE) # Optional[UserProfile]
|
||||||
streams = models.ManyToManyField('Stream') # type: Manager
|
streams = models.ManyToManyField('Stream') # type: Manager
|
||||||
|
|||||||
@@ -443,9 +443,8 @@ class InviteUserBase(ZulipTestCase):
|
|||||||
self.assertIn(FromAddress.NOREPLY, outbox[0].from_email)
|
self.assertIn(FromAddress.NOREPLY, outbox[0].from_email)
|
||||||
|
|
||||||
class InviteUserTest(InviteUserBase):
|
class InviteUserTest(InviteUserBase):
|
||||||
|
def invite(self, users, streams, body='', invite_as_admin="false"):
|
||||||
def invite(self, users, streams, body=''):
|
# type: (Text, List[Text], str, str) -> HttpResponse
|
||||||
# type: (Text, List[Text], str) -> HttpResponse
|
|
||||||
"""
|
"""
|
||||||
Invites the specified users to Zulip with the specified streams.
|
Invites the specified users to Zulip with the specified streams.
|
||||||
|
|
||||||
@@ -458,6 +457,7 @@ class InviteUserTest(InviteUserBase):
|
|||||||
return self.client_post("/json/invites",
|
return self.client_post("/json/invites",
|
||||||
{"invitee_emails": users,
|
{"invitee_emails": users,
|
||||||
"stream": streams,
|
"stream": streams,
|
||||||
|
"invite_as_admin": invite_as_admin,
|
||||||
"custom_body": body})
|
"custom_body": body})
|
||||||
|
|
||||||
def test_successful_invite_user(self):
|
def test_successful_invite_user(self):
|
||||||
@@ -472,6 +472,32 @@ class InviteUserTest(InviteUserBase):
|
|||||||
self.assertTrue(find_key_by_email(invitee))
|
self.assertTrue(find_key_by_email(invitee))
|
||||||
self.check_sent_emails([invitee], custom_from_name="Hamlet")
|
self.check_sent_emails([invitee], custom_from_name="Hamlet")
|
||||||
|
|
||||||
|
def test_successful_invite_user_as_admin_from_admin_account(self):
|
||||||
|
# type: () -> None
|
||||||
|
"""
|
||||||
|
Test that a new user invited to a stream receives some initial
|
||||||
|
history but only from public streams.
|
||||||
|
"""
|
||||||
|
self.login(self.example_email('iago'))
|
||||||
|
invitee = self.nonreg_email('alice')
|
||||||
|
self.assert_json_success(self.invite(invitee, ["Denmark"], invite_as_admin="true"))
|
||||||
|
self.assertTrue(find_key_by_email(invitee))
|
||||||
|
|
||||||
|
self.submit_reg_form_for_user(invitee, "password")
|
||||||
|
invitee_profile = self.nonreg_user('alice')
|
||||||
|
self.assertTrue(invitee_profile.is_realm_admin)
|
||||||
|
|
||||||
|
def test_invite_user_as_admin_from_normal_account(self):
|
||||||
|
# type: () -> None
|
||||||
|
"""
|
||||||
|
Test that a new user invited to a stream receives some initial
|
||||||
|
history but only from public streams.
|
||||||
|
"""
|
||||||
|
self.login(self.example_email('hamlet'))
|
||||||
|
invitee = self.nonreg_email('alice')
|
||||||
|
response = self.invite(invitee, ["Denmark"], invite_as_admin="true")
|
||||||
|
self.assert_json_error(response, "Must be a realm administrator")
|
||||||
|
|
||||||
def test_successful_invite_user_with_custom_body(self):
|
def test_successful_invite_user_with_custom_body(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
"""
|
"""
|
||||||
@@ -583,7 +609,7 @@ class InviteUserTest(InviteUserBase):
|
|||||||
UserMessage.objects.filter(user_profile=invitee_profile)]
|
UserMessage.objects.filter(user_profile=invitee_profile)]
|
||||||
self.assertTrue(public_msg_id in invitee_msg_ids)
|
self.assertTrue(public_msg_id in invitee_msg_ids)
|
||||||
self.assertFalse(secret_msg_id in invitee_msg_ids)
|
self.assertFalse(secret_msg_id in invitee_msg_ids)
|
||||||
|
self.assertFalse(invitee_profile.is_realm_admin)
|
||||||
# Test that exactly 2 new Zulip messages were sent, both notifications.
|
# Test that exactly 2 new Zulip messages were sent, both notifications.
|
||||||
last_3_messages = list(reversed(list(Message.objects.all().order_by("-id")[0:3])))
|
last_3_messages = list(reversed(list(Message.objects.all().order_by("-id")[0:3])))
|
||||||
first_msg = last_3_messages[0]
|
first_msg = last_3_messages[0]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from zerver.lib.actions import do_invite_users, \
|
|||||||
from zerver.lib.request import REQ, has_request_variables, JsonableError
|
from zerver.lib.request import REQ, has_request_variables, JsonableError
|
||||||
from zerver.lib.response import json_success, json_error
|
from zerver.lib.response import json_success, json_error
|
||||||
from zerver.lib.streams import access_stream_by_name
|
from zerver.lib.streams import access_stream_by_name
|
||||||
from zerver.lib.validator import check_string, check_list
|
from zerver.lib.validator import check_string, check_list, check_bool
|
||||||
from zerver.models import PreregistrationUser, Stream, UserProfile
|
from zerver.models import PreregistrationUser, Stream, UserProfile
|
||||||
|
|
||||||
import re
|
import re
|
||||||
@@ -19,10 +19,14 @@ import re
|
|||||||
@has_request_variables
|
@has_request_variables
|
||||||
def invite_users_backend(request, user_profile,
|
def invite_users_backend(request, user_profile,
|
||||||
invitee_emails_raw=REQ("invitee_emails"),
|
invitee_emails_raw=REQ("invitee_emails"),
|
||||||
|
invite_as_admin=REQ(validator=check_bool, default=False),
|
||||||
body=REQ("custom_body", default=None)):
|
body=REQ("custom_body", default=None)):
|
||||||
# type: (HttpRequest, UserProfile, str, Optional[str]) -> HttpResponse
|
# type: (HttpRequest, UserProfile, str, Optional[bool], Optional[str]) -> HttpResponse
|
||||||
|
|
||||||
if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
|
if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
|
||||||
return json_error(_("Must be a realm administrator"))
|
return json_error(_("Must be a realm administrator"))
|
||||||
|
if invite_as_admin and not user_profile.is_realm_admin:
|
||||||
|
return json_error(_("Must be a realm administrator"))
|
||||||
if not invitee_emails_raw:
|
if not invitee_emails_raw:
|
||||||
return json_error(_("You must specify at least one email address."))
|
return json_error(_("You must specify at least one email address."))
|
||||||
if body == '':
|
if body == '':
|
||||||
@@ -48,7 +52,7 @@ def invite_users_backend(request, user_profile,
|
|||||||
return json_error(_("Stream does not exist: %s. No invites were sent.") % (stream_name,))
|
return json_error(_("Stream does not exist: %s. No invites were sent.") % (stream_name,))
|
||||||
streams.append(stream)
|
streams.append(stream)
|
||||||
|
|
||||||
do_invite_users(user_profile, invitee_emails, streams, body)
|
do_invite_users(user_profile, invitee_emails, streams, invite_as_admin, body)
|
||||||
return json_success()
|
return json_success()
|
||||||
|
|
||||||
def get_invitee_emails_set(invitee_emails_raw):
|
def get_invitee_emails_set(invitee_emails_raw):
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ def accounts_register(request):
|
|||||||
email = prereg_user.email
|
email = prereg_user.email
|
||||||
realm_creation = prereg_user.realm_creation
|
realm_creation = prereg_user.realm_creation
|
||||||
password_required = prereg_user.password_required
|
password_required = prereg_user.password_required
|
||||||
|
is_realm_admin = prereg_user.invited_as_admin or realm_creation
|
||||||
|
|
||||||
validators.validate_email(email)
|
validators.validate_email(email)
|
||||||
if prereg_user.referred_by:
|
if prereg_user.referred_by:
|
||||||
@@ -228,8 +229,9 @@ def accounts_register(request):
|
|||||||
do_change_full_name(user_profile, full_name, user_profile)
|
do_change_full_name(user_profile, full_name, user_profile)
|
||||||
do_set_user_display_setting(user_profile, 'timezone', timezone)
|
do_set_user_display_setting(user_profile, 'timezone', timezone)
|
||||||
else:
|
else:
|
||||||
|
# TODO: When we clean up this code path, make it respect is_realm_admin.
|
||||||
user_profile = do_create_user(email, password, realm, full_name, short_name,
|
user_profile = do_create_user(email, password, realm, full_name, short_name,
|
||||||
prereg_user=prereg_user, is_realm_admin=realm_creation,
|
prereg_user=prereg_user, is_realm_admin=is_realm_admin,
|
||||||
tos_version=settings.TOS_VERSION,
|
tos_version=settings.TOS_VERSION,
|
||||||
timezone=timezone,
|
timezone=timezone,
|
||||||
newsletter_data={"IP": request.META['REMOTE_ADDR']},
|
newsletter_data={"IP": request.META['REMOTE_ADDR']},
|
||||||
|
|||||||
Reference in New Issue
Block a user