mirror of https://github.com/zulip/zulip.git
invites: Add UI for revoking multiuse invites.
This commit is contained in:
parent
03dcace09d
commit
763eca6ca9
|
@ -294,9 +294,6 @@ run_test('admin_invites_list', () => {
|
||||||
|
|
||||||
var span = $(html).find(".email:first");
|
var span = $(html).find(".email:first");
|
||||||
assert.equal(span.text(), "alice@zulip.com");
|
assert.equal(span.text(), "alice@zulip.com");
|
||||||
|
|
||||||
var icon = $(html).find(".fa-bolt");
|
|
||||||
assert.equal(icon.attr('title'), "translated: Invited as administrator");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test('admin_tab', () => {
|
run_test('admin_tab', () => {
|
||||||
|
@ -1050,6 +1047,18 @@ run_test('reminder_popover_content', () => {
|
||||||
assert.equal(link.text().trim(), 'translated: Select date and time');
|
assert.equal(link.text().trim(), 'translated: Select date and time');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
run_test('revoke_invite_modal', () => {
|
||||||
|
var args = {
|
||||||
|
is_multiuse: false,
|
||||||
|
email: "iago@zulip.com",
|
||||||
|
};
|
||||||
|
|
||||||
|
var html = "<div>";
|
||||||
|
html += render('revoke-invite-modal', args);
|
||||||
|
html += "</div>";
|
||||||
|
assert.equal($(html).find("p strong").text(), "iago@zulip.com");
|
||||||
|
});
|
||||||
|
|
||||||
run_test('settings_tab', () => {
|
run_test('settings_tab', () => {
|
||||||
var page_param_checkbox_options = {
|
var page_param_checkbox_options = {
|
||||||
enable_stream_desktop_notifications: true,
|
enable_stream_desktop_notifications: true,
|
||||||
|
|
|
@ -50,7 +50,7 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
|
||||||
|
|
||||||
case 'invites_changed':
|
case 'invites_changed':
|
||||||
if ($('#admin-invites-list').length) {
|
if ($('#admin-invites-list').length) {
|
||||||
settings_invites.set_up();
|
settings_invites.set_up(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,12 @@ function populate_invites(invites_data) {
|
||||||
filter: {
|
filter: {
|
||||||
element: invites_table.closest(".settings-section").find(".search"),
|
element: invites_table.closest(".settings-section").find(".search"),
|
||||||
callback: function (item, value) {
|
callback: function (item, value) {
|
||||||
return item.email.toLowerCase().indexOf(value) >= 0;
|
var referrer_email_matched = item.ref.toLowerCase().indexOf(value) >= 0;
|
||||||
|
if (item.is_multiuse) {
|
||||||
|
return referrer_email_matched;
|
||||||
|
}
|
||||||
|
var invitee_email_matched = item.email.toLowerCase().indexOf(value) >= 0;
|
||||||
|
return referrer_email_matched || invitee_email_matched;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).init();
|
}).init();
|
||||||
|
@ -76,17 +81,23 @@ function populate_invites(invites_data) {
|
||||||
|
|
||||||
function do_revoke_invite() {
|
function do_revoke_invite() {
|
||||||
var modal_invite_id = $("#revoke_invite_modal #do_revoke_invite_button").attr("data-invite-id");
|
var modal_invite_id = $("#revoke_invite_modal #do_revoke_invite_button").attr("data-invite-id");
|
||||||
|
var modal_is_multiuse = $("#revoke_invite_modal #do_revoke_invite_button").attr("data-is-multiuse");
|
||||||
var revoke_button = meta.current_revoke_invite_user_modal_row.find("button.revoke");
|
var revoke_button = meta.current_revoke_invite_user_modal_row.find("button.revoke");
|
||||||
|
|
||||||
if (modal_invite_id !== meta.invite_id) {
|
if (modal_invite_id !== meta.invite_id || modal_is_multiuse !== meta.is_multiuse) {
|
||||||
blueslip.error("Invite revoking canceled due to non-matching fields.");
|
blueslip.error("Invite revoking canceled due to non-matching fields.");
|
||||||
ui_report.message(i18n.t("Resending encountered an error. Please reload and try again."),
|
ui_report.message(i18n.t("Resending encountered an error. Please reload and try again."),
|
||||||
$("#home-error"), 'alert-error');
|
$("#home-error"), 'alert-error');
|
||||||
}
|
}
|
||||||
$("#revoke_invite_modal").modal("hide");
|
$("#revoke_invite_modal").modal("hide");
|
||||||
revoke_button.prop("disabled", true).text(i18n.t("Working…"));
|
revoke_button.prop("disabled", true).text(i18n.t("Working…"));
|
||||||
|
var url = '/json/invites/' + meta.invite_id;
|
||||||
|
|
||||||
|
if (modal_is_multiuse === "true") {
|
||||||
|
url = '/json/invites/multiuse/' + meta.invite_id;
|
||||||
|
}
|
||||||
channel.del({
|
channel.del({
|
||||||
url: '/json/invites/' + meta.invite_id,
|
url: url,
|
||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
ui_report.generic_row_button_error(xhr, revoke_button);
|
ui_report.generic_row_button_error(xhr, revoke_button);
|
||||||
},
|
},
|
||||||
|
@ -96,8 +107,11 @@ function do_revoke_invite() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.set_up = function () {
|
exports.set_up = function (initialize_event_handlers) {
|
||||||
meta.loaded = true;
|
meta.loaded = true;
|
||||||
|
if (typeof initialize_event_handlers === 'undefined') {
|
||||||
|
initialize_event_handlers = true;
|
||||||
|
}
|
||||||
|
|
||||||
// create loading indicators
|
// create loading indicators
|
||||||
loading.make_indicator($('#admin_page_invites_loading_indicator'));
|
loading.make_indicator($('#admin_page_invites_loading_indicator'));
|
||||||
|
@ -107,29 +121,38 @@ exports.set_up = function () {
|
||||||
url: '/json/invites',
|
url: '/json/invites',
|
||||||
idempotent: true,
|
idempotent: true,
|
||||||
timeout: 10 * 1000,
|
timeout: 10 * 1000,
|
||||||
success: exports.on_load_success,
|
success: function (data) {
|
||||||
|
exports.on_load_success(data, initialize_event_handlers);
|
||||||
|
},
|
||||||
error: failed_listing_invites,
|
error: failed_listing_invites,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.on_load_success = function (invites_data) {
|
exports.on_load_success = function (invites_data, initialize_event_handlers) {
|
||||||
meta.loaded = true;
|
meta.loaded = true;
|
||||||
populate_invites(invites_data);
|
populate_invites(invites_data);
|
||||||
|
if (!initialize_event_handlers) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$(".admin_invites_table").on("click", ".revoke", function (e) {
|
$(".admin_invites_table").on("click", ".revoke", function (e) {
|
||||||
// This click event must not get propagated to parent container otherwise the modal
|
// This click event must not get propagated to parent container otherwise the modal
|
||||||
// will not show up because of a call to `close_active_modal` in `settings.js`.
|
// will not show up because of a call to `close_active_modal` in `settings.js`.
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
var row = $(e.target).closest(".invite_row");
|
var row = $(e.target).closest(".invite_row");
|
||||||
var email = row.find('.email').text();
|
var email = row.find('.email').text();
|
||||||
|
var referred_by = row.find('.referred_by').text();
|
||||||
meta.current_revoke_invite_user_modal_row = row;
|
meta.current_revoke_invite_user_modal_row = row;
|
||||||
meta.invite_id = $(e.currentTarget).attr("data-invite-id");
|
meta.invite_id = $(e.currentTarget).attr("data-invite-id");
|
||||||
|
meta.is_multiuse = $(e.currentTarget).attr("data-is-multiuse");
|
||||||
$("#revoke_invite_modal .email").text(email);
|
var ctx = {is_multiuse: meta.is_multiuse === "true", email: email, referred_by: referred_by};
|
||||||
|
var rendered_revoke_modal = templates.render("revoke-invite-modal", ctx);
|
||||||
|
$("#revoke_invite_modal_holder").html(rendered_revoke_modal);
|
||||||
$("#revoke_invite_modal #do_revoke_invite_button").attr("data-invite-id", meta.invite_id);
|
$("#revoke_invite_modal #do_revoke_invite_button").attr("data-invite-id", meta.invite_id);
|
||||||
|
$("#revoke_invite_modal #do_revoke_invite_button").attr("data-is-multiuse", meta.is_multiuse);
|
||||||
$("#revoke_invite_modal").modal("show");
|
$("#revoke_invite_modal").modal("show");
|
||||||
|
$("#do_revoke_invite_button").unbind("click");
|
||||||
|
$("#do_revoke_invite_button").click(do_revoke_invite);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".admin_invites_table").on("click", ".resend", function (e) {
|
$(".admin_invites_table").on("click", ".resend", function (e) {
|
||||||
|
@ -148,8 +171,6 @@ exports.on_load_success = function (invites_data) {
|
||||||
$("#resend_invite_modal").modal("show");
|
$("#resend_invite_modal").modal("show");
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#do_revoke_invite_button").click(do_revoke_invite);
|
|
||||||
|
|
||||||
$("#do_resend_invite_button").click(function () {
|
$("#do_resend_invite_button").click(function () {
|
||||||
var modal_invite_id = $("#resend_invite_modal #do_resend_invite_button").attr("data-invite-id");
|
var modal_invite_id = $("#resend_invite_modal #do_resend_invite_button").attr("data-invite-id");
|
||||||
var resend_button = meta.current_resend_invite_user_modal_row.find("button.resend");
|
var resend_button = meta.current_resend_invite_user_modal_row.find("button.resend");
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
{{#with invite}}
|
{{#with invite}}
|
||||||
<tr class="invite_row">
|
<tr class="invite_row">
|
||||||
<td>
|
<td>
|
||||||
|
{{#if is_multiuse}}
|
||||||
|
<span class="email">{{t 'Invite link'}}</span>
|
||||||
|
{{else}}
|
||||||
<span class="email">{{email}}</span>
|
<span class="email">{{email}}</span>
|
||||||
{{#if invited_as_admin}}
|
|
||||||
<i title="{{t 'Invited as administrator'}}" class="fa fa-bolt invited-as-admin"></i>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -16,12 +17,14 @@
|
||||||
<span>{{invited_as_text}}</span>
|
<span>{{invited_as_text}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="button rounded small revoke btn-danger" data-invite-id="{{id}}">
|
<button class="button rounded small revoke btn-danger" data-invite-id="{{id}}" data-is-multiuse="{{is_multiuse}}">
|
||||||
{{t "Revoke" }}
|
{{t "Revoke" }}
|
||||||
</button>
|
</button>
|
||||||
|
{{#unless is_multiuse}}
|
||||||
<button class="button rounded small resend btn-warning" data-invite-id="{{id}}">
|
<button class="button rounded small resend btn-warning" data-invite-id="{{id}}">
|
||||||
{{t "Resend" }}
|
{{t "Resend" }}
|
||||||
</button>
|
</button>
|
||||||
|
{{/unless}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/with}}
|
{{/with}}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<div class="alert" id="organization-status"></div>
|
<div class="alert" id="organization-status"></div>
|
||||||
|
<div id="revoke_invite_modal_holder"></div>
|
||||||
|
|
||||||
{{ partial "admin-settings-modals"}}
|
{{ partial "admin-settings-modals"}}
|
||||||
|
|
||||||
{{ partial "organization-profile-admin" }}
|
{{ partial "organization-profile-admin" }}
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
<div id="revoke_invite_modal" class="modal modal-bg hide fade" tabindex="-1" role="dialog" aria-labelledby="revoke_invite_modal_label" aria-hidden="true">
|
<div id="revoke_invite_modal" class="modal modal-bg hide fade" tabindex="-1" role="dialog" aria-labelledby="revoke_invite_modal_label" aria-hidden="true">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{t 'Close' }}"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="{{t 'Close' }}"><span aria-hidden="true">×</span></button>
|
||||||
<h3 id="revoke_invite_modal_label">{{#tr this}}Revoke invitation to <span class="email"></span>{{/tr}}</h3>
|
{{#if is_multiuse}}
|
||||||
|
<h3 id="revoke_invite_modal_label">{{#tr this}}Revoke invitation link{{/tr}}</h3>
|
||||||
|
{{else}}
|
||||||
|
<h3 id="revoke_invite_modal_label">{{#tr this}}Revoke invitation to __email__{{/tr}}</h3>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body" id="revoke_invite_message">
|
||||||
<p>{{#tr this}}Are you sure you want to revoke the invitation to <strong><span class="email"></span></strong>?{{/tr}}</p>
|
{{#if is_multiuse}}
|
||||||
|
<p>{{#tr this}}Are you sure you want to revoke this invitation link created by <strong>__referred_by__</strong>?{{/tr}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p>{{#tr this}}Are you sure you want to revoke the invitation to <strong>__email__</strong>?{{/tr}}</p>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="button rounded" data-dismiss="modal">{{t "Cancel" }}</button>
|
<button class="button rounded" data-dismiss="modal">{{t "Cancel" }}</button>
|
||||||
|
|
|
@ -4902,8 +4902,19 @@ def do_get_user_invites(user_profile: UserProfile) -> List[Dict[str, Any]]:
|
||||||
ref=invitee.referred_by.email,
|
ref=invitee.referred_by.email,
|
||||||
invited=datetime_to_timestamp(invitee.invited_at),
|
invited=datetime_to_timestamp(invitee.invited_at),
|
||||||
id=invitee.id,
|
id=invitee.id,
|
||||||
invited_as=invitee.invited_as))
|
invited_as=invitee.invited_as,
|
||||||
|
is_multiuse=False))
|
||||||
|
|
||||||
|
multiuse_confirmation_objs = Confirmation.objects.filter(realm=user_profile.realm,
|
||||||
|
type=Confirmation.MULTIUSE_INVITE,
|
||||||
|
date_sent__gte=lowest_datetime)
|
||||||
|
for confirmation_obj in multiuse_confirmation_objs:
|
||||||
|
invite = confirmation_obj.content_object
|
||||||
|
invites.append(dict(ref=invite.referred_by.email,
|
||||||
|
invited=datetime_to_timestamp(confirmation_obj.date_sent),
|
||||||
|
id=invite.id,
|
||||||
|
invited_as=invite.invited_as,
|
||||||
|
is_multiuse=True))
|
||||||
return invites
|
return invites
|
||||||
|
|
||||||
def do_create_multiuse_invite_link(referred_by: UserProfile, invited_as: int,
|
def do_create_multiuse_invite_link(referred_by: UserProfile, invited_as: int,
|
||||||
|
@ -4914,6 +4925,7 @@ def do_create_multiuse_invite_link(referred_by: UserProfile, invited_as: int,
|
||||||
invite.streams.set(streams)
|
invite.streams.set(streams)
|
||||||
invite.invited_as = invited_as
|
invite.invited_as = invited_as
|
||||||
invite.save()
|
invite.save()
|
||||||
|
notify_invites_changed(referred_by)
|
||||||
return create_confirmation_link(invite, realm.host, Confirmation.MULTIUSE_INVITE)
|
return create_confirmation_link(invite, realm.host, Confirmation.MULTIUSE_INVITE)
|
||||||
|
|
||||||
def do_revoke_user_invite(prereg_user: PreregistrationUser) -> None:
|
def do_revoke_user_invite(prereg_user: PreregistrationUser) -> None:
|
||||||
|
@ -4921,7 +4933,7 @@ def do_revoke_user_invite(prereg_user: PreregistrationUser) -> None:
|
||||||
|
|
||||||
# Delete both the confirmation objects and the prereg_user object.
|
# Delete both the confirmation objects and the prereg_user object.
|
||||||
# TODO: Probably we actaully want to set the confirmation objects
|
# TODO: Probably we actaully want to set the confirmation objects
|
||||||
# to a "revoked" status so that we can give the user a better
|
# to a "revoked" status so that we can give the invited user a better
|
||||||
# error message.
|
# error message.
|
||||||
content_type = ContentType.objects.get_for_model(PreregistrationUser)
|
content_type = ContentType.objects.get_for_model(PreregistrationUser)
|
||||||
Confirmation.objects.filter(content_type=content_type,
|
Confirmation.objects.filter(content_type=content_type,
|
||||||
|
@ -4930,6 +4942,13 @@ def do_revoke_user_invite(prereg_user: PreregistrationUser) -> None:
|
||||||
clear_scheduled_invitation_emails(email)
|
clear_scheduled_invitation_emails(email)
|
||||||
notify_invites_changed(prereg_user)
|
notify_invites_changed(prereg_user)
|
||||||
|
|
||||||
|
def do_revoke_multi_use_invite(multiuse_invite: MultiuseInvite) -> None:
|
||||||
|
content_type = ContentType.objects.get_for_model(MultiuseInvite)
|
||||||
|
Confirmation.objects.filter(content_type=content_type,
|
||||||
|
object_id=multiuse_invite.id).delete()
|
||||||
|
multiuse_invite.delete()
|
||||||
|
notify_invites_changed(multiuse_invite.referred_by)
|
||||||
|
|
||||||
def do_resend_user_invite_email(prereg_user: PreregistrationUser) -> int:
|
def do_resend_user_invite_email(prereg_user: PreregistrationUser) -> int:
|
||||||
check_invite_limit(prereg_user.referred_by.realm, 1)
|
check_invite_limit(prereg_user.referred_by.realm, 1)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ from zerver.models import (
|
||||||
get_client, get_realm, get_stream_recipient, get_stream,
|
get_client, get_realm, get_stream_recipient, get_stream,
|
||||||
Message, RealmDomain, Recipient, UserMessage, UserPresence, UserProfile,
|
Message, RealmDomain, Recipient, UserMessage, UserPresence, UserProfile,
|
||||||
Realm, Subscription, Stream, flush_per_request_caches, UserGroup, Service,
|
Realm, Subscription, Stream, flush_per_request_caches, UserGroup, Service,
|
||||||
Attachment, PreregistrationUser, get_user_by_delivery_email
|
Attachment, PreregistrationUser, get_user_by_delivery_email, MultiuseInvite
|
||||||
)
|
)
|
||||||
|
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
|
@ -51,6 +51,7 @@ from zerver.lib.actions import (
|
||||||
do_change_user_delivery_email,
|
do_change_user_delivery_email,
|
||||||
do_create_user,
|
do_create_user,
|
||||||
do_create_default_stream_group,
|
do_create_default_stream_group,
|
||||||
|
do_create_multiuse_invite_link,
|
||||||
do_deactivate_stream,
|
do_deactivate_stream,
|
||||||
do_deactivate_user,
|
do_deactivate_user,
|
||||||
do_delete_messages,
|
do_delete_messages,
|
||||||
|
@ -69,6 +70,7 @@ from zerver.lib.actions import (
|
||||||
do_remove_realm_filter,
|
do_remove_realm_filter,
|
||||||
do_remove_streams_from_default_stream_group,
|
do_remove_streams_from_default_stream_group,
|
||||||
do_rename_stream,
|
do_rename_stream,
|
||||||
|
do_revoke_multi_use_invite,
|
||||||
do_revoke_user_invite,
|
do_revoke_user_invite,
|
||||||
do_set_realm_authentication_methods,
|
do_set_realm_authentication_methods,
|
||||||
do_set_realm_message_editing,
|
do_set_realm_message_editing,
|
||||||
|
@ -947,6 +949,23 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
error = schema_checker('events[0]', events[0])
|
error = schema_checker('events[0]', events[0])
|
||||||
self.assert_on_error(error)
|
self.assert_on_error(error)
|
||||||
|
|
||||||
|
def test_create_multiuse_invite_event(self) -> None:
|
||||||
|
schema_checker = self.check_events_dict([
|
||||||
|
('type', equals('invites_changed')),
|
||||||
|
])
|
||||||
|
|
||||||
|
self.user_profile = self.example_user('iago')
|
||||||
|
streams = []
|
||||||
|
for stream_name in ["Denmark", "Verona"]:
|
||||||
|
streams.append(get_stream(stream_name, self.user_profile.realm))
|
||||||
|
|
||||||
|
events = self.do_test(
|
||||||
|
lambda: do_create_multiuse_invite_link(self.user_profile, PreregistrationUser.INVITE_AS['MEMBER'], streams),
|
||||||
|
state_change_expected=False,
|
||||||
|
)
|
||||||
|
error = schema_checker('events[0]', events[0])
|
||||||
|
self.assert_on_error(error)
|
||||||
|
|
||||||
def test_revoke_user_invite_event(self) -> None:
|
def test_revoke_user_invite_event(self) -> None:
|
||||||
schema_checker = self.check_events_dict([
|
schema_checker = self.check_events_dict([
|
||||||
('type', equals('invites_changed')),
|
('type', equals('invites_changed')),
|
||||||
|
@ -965,6 +984,25 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
error = schema_checker('events[0]', events[0])
|
error = schema_checker('events[0]', events[0])
|
||||||
self.assert_on_error(error)
|
self.assert_on_error(error)
|
||||||
|
|
||||||
|
def test_revoke_multiuse_invite_event(self) -> None:
|
||||||
|
schema_checker = self.check_events_dict([
|
||||||
|
('type', equals('invites_changed')),
|
||||||
|
])
|
||||||
|
|
||||||
|
self.user_profile = self.example_user('iago')
|
||||||
|
streams = []
|
||||||
|
for stream_name in ["Denmark", "Verona"]:
|
||||||
|
streams.append(get_stream(stream_name, self.user_profile.realm))
|
||||||
|
do_create_multiuse_invite_link(self.user_profile, PreregistrationUser.INVITE_AS['MEMBER'], streams)
|
||||||
|
|
||||||
|
multiuse_object = MultiuseInvite.objects.get()
|
||||||
|
events = self.do_test(
|
||||||
|
lambda: do_revoke_multi_use_invite(multiuse_object),
|
||||||
|
state_change_expected=False,
|
||||||
|
)
|
||||||
|
error = schema_checker('events[0]', events[0])
|
||||||
|
self.assert_on_error(error)
|
||||||
|
|
||||||
def test_invitation_accept_invite_event(self) -> None:
|
def test_invitation_accept_invite_event(self) -> None:
|
||||||
schema_checker = self.check_events_dict([
|
schema_checker = self.check_events_dict([
|
||||||
('type', equals('invites_changed')),
|
('type', equals('invites_changed')),
|
||||||
|
|
|
@ -1254,7 +1254,7 @@ class InvitationsTestCase(InviteUserBase):
|
||||||
"""
|
"""
|
||||||
A GET call to /json/invites returns all unexpired invitations.
|
A GET call to /json/invites returns all unexpired invitations.
|
||||||
"""
|
"""
|
||||||
|
realm = get_realm("zulip")
|
||||||
days_to_activate = getattr(settings, 'INVITATION_LINK_VALIDITY_DAYS', "Wrong")
|
days_to_activate = getattr(settings, 'INVITATION_LINK_VALIDITY_DAYS', "Wrong")
|
||||||
active_value = getattr(confirmation_settings, 'STATUS_ACTIVE', "Wrong")
|
active_value = getattr(confirmation_settings, 'STATUS_ACTIVE', "Wrong")
|
||||||
self.assertNotEqual(days_to_activate, "Wrong")
|
self.assertNotEqual(days_to_activate, "Wrong")
|
||||||
|
@ -1273,10 +1273,19 @@ class InvitationsTestCase(InviteUserBase):
|
||||||
referred_by=user_profile, status=active_value)
|
referred_by=user_profile, status=active_value)
|
||||||
prereg_user_three.save()
|
prereg_user_three.save()
|
||||||
|
|
||||||
|
multiuse_invite_one = MultiuseInvite.objects.create(referred_by=self.example_user("hamlet"), realm=realm)
|
||||||
|
create_confirmation_link(multiuse_invite_one, realm.host, Confirmation.MULTIUSE_INVITE)
|
||||||
|
|
||||||
|
multiuse_invite_two = MultiuseInvite.objects.create(referred_by=self.example_user("othello"), realm=realm)
|
||||||
|
create_confirmation_link(multiuse_invite_two, realm.host, Confirmation.MULTIUSE_INVITE)
|
||||||
|
confirmation = Confirmation.objects.last()
|
||||||
|
confirmation.date_sent = expired_datetime
|
||||||
|
confirmation.save()
|
||||||
|
|
||||||
result = self.client_get("/json/invites")
|
result = self.client_get("/json/invites")
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
self.assert_in_success_response(["TestOne@zulip.com"], result)
|
self.assert_in_success_response(["TestOne@zulip.com", "hamlet@zulip.com"], result)
|
||||||
self.assert_not_in_success_response(["TestTwo@zulip.com", "TestThree@zulip.com"], result)
|
self.assert_not_in_success_response(["TestTwo@zulip.com", "TestThree@zulip.com", "othello@zulip.com"], result)
|
||||||
|
|
||||||
def test_successful_delete_invitation(self) -> None:
|
def test_successful_delete_invitation(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1302,6 +1311,30 @@ class InvitationsTestCase(InviteUserBase):
|
||||||
lambda: ScheduledEmail.objects.get(address__iexact=invitee,
|
lambda: ScheduledEmail.objects.get(address__iexact=invitee,
|
||||||
type=ScheduledEmail.INVITATION_REMINDER))
|
type=ScheduledEmail.INVITATION_REMINDER))
|
||||||
|
|
||||||
|
def test_delete_multiuse_invite(self) -> None:
|
||||||
|
"""
|
||||||
|
A DELETE call to /json/invites/multiuse<ID> should delete the
|
||||||
|
multiuse_invite.
|
||||||
|
"""
|
||||||
|
self.login(self.example_email("iago"))
|
||||||
|
|
||||||
|
zulip_realm = get_realm("zulip")
|
||||||
|
multiuse_invite = MultiuseInvite.objects.create(referred_by=self.example_user("hamlet"), realm=zulip_realm)
|
||||||
|
create_confirmation_link(multiuse_invite, zulip_realm.host, Confirmation.MULTIUSE_INVITE)
|
||||||
|
result = self.client_delete('/json/invites/multiuse/' + str(multiuse_invite.id))
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
self.assertIsNone(MultiuseInvite.objects.filter(id=multiuse_invite.id).first())
|
||||||
|
# Test that trying to double-delete fails
|
||||||
|
error_result = self.client_delete('/json/invites/multiuse/' + str(multiuse_invite.id))
|
||||||
|
self.assert_json_error(error_result, "No such invitation")
|
||||||
|
|
||||||
|
# Test deleting multiuse invite from another realm
|
||||||
|
mit_realm = get_realm("zephyr")
|
||||||
|
multiuse_invite_in_mit = MultiuseInvite.objects.create(referred_by=self.mit_user("sipbtest"), realm=mit_realm)
|
||||||
|
create_confirmation_link(multiuse_invite_in_mit, mit_realm.host, Confirmation.MULTIUSE_INVITE)
|
||||||
|
error_result = self.client_delete('/json/invites/multiuse/' + str(multiuse_invite_in_mit.id))
|
||||||
|
self.assert_json_error(error_result, "No such invitation")
|
||||||
|
|
||||||
def test_successful_resend_invitation(self) -> None:
|
def test_successful_resend_invitation(self) -> None:
|
||||||
"""
|
"""
|
||||||
A POST call to /json/invites/<ID>/resend should send an invitation reminder email
|
A POST call to /json/invites/<ID>/resend should send an invitation reminder email
|
||||||
|
|
|
@ -2,16 +2,15 @@ from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from typing import List, Optional, Set
|
from typing import List, Optional, Set
|
||||||
|
|
||||||
from zerver.decorator import require_realm_admin, \
|
from zerver.decorator import require_realm_admin, require_non_guest_human_user
|
||||||
require_non_guest_human_user
|
from zerver.lib.actions import do_invite_users, do_revoke_user_invite, \
|
||||||
|
do_revoke_multi_use_invite, do_resend_user_invite_email, \
|
||||||
from zerver.lib.actions import do_invite_users, do_revoke_user_invite, do_resend_user_invite_email, \
|
get_default_subs, do_get_user_invites, do_create_multiuse_invite_link
|
||||||
do_get_user_invites, do_create_multiuse_invite_link
|
|
||||||
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, json_response
|
||||||
from zerver.lib.streams import access_stream_by_id
|
from zerver.lib.streams import access_stream_by_id
|
||||||
from zerver.lib.validator import check_list, check_int
|
from zerver.lib.validator import check_string, check_list, check_bool, check_int
|
||||||
from zerver.models import PreregistrationUser, Stream, UserProfile
|
from zerver.models import PreregistrationUser, Stream, UserProfile, MultiuseInvite
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -85,6 +84,22 @@ def revoke_user_invite(request: HttpRequest, user_profile: UserProfile,
|
||||||
do_revoke_user_invite(prereg_user)
|
do_revoke_user_invite(prereg_user)
|
||||||
return json_success()
|
return json_success()
|
||||||
|
|
||||||
|
@require_realm_admin
|
||||||
|
@has_request_variables
|
||||||
|
def revoke_multiuse_invite(request: HttpRequest, user_profile: UserProfile,
|
||||||
|
invite_id: int) -> HttpResponse:
|
||||||
|
|
||||||
|
try:
|
||||||
|
invite = MultiuseInvite.objects.get(id=invite_id)
|
||||||
|
except MultiuseInvite.DoesNotExist:
|
||||||
|
raise JsonableError(_("No such invitation"))
|
||||||
|
|
||||||
|
if invite.realm != user_profile.realm:
|
||||||
|
raise JsonableError(_("No such invitation"))
|
||||||
|
|
||||||
|
do_revoke_multi_use_invite(invite)
|
||||||
|
return json_success()
|
||||||
|
|
||||||
@require_realm_admin
|
@require_realm_admin
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def resend_user_invite_email(request: HttpRequest, user_profile: UserProfile,
|
def resend_user_invite_email(request: HttpRequest, user_profile: UserProfile,
|
||||||
|
|
|
@ -165,6 +165,10 @@ v1_api_and_json_patterns = [
|
||||||
# invites/multiuse -> zerver.views.invite
|
# invites/multiuse -> zerver.views.invite
|
||||||
url(r'^invites/multiuse$', rest_dispatch,
|
url(r'^invites/multiuse$', rest_dispatch,
|
||||||
{'POST': 'zerver.views.invite.generate_multiuse_invite_backend'}),
|
{'POST': 'zerver.views.invite.generate_multiuse_invite_backend'}),
|
||||||
|
# invites/multiuse -> zerver.views.invite
|
||||||
|
url(r'^invites/multiuse/(?P<invite_id>[0-9]+)$', rest_dispatch,
|
||||||
|
{'DELETE': 'zerver.views.invite.revoke_multiuse_invite'}),
|
||||||
|
|
||||||
# mark messages as read (in bulk)
|
# mark messages as read (in bulk)
|
||||||
url(r'^mark_all_as_read$', rest_dispatch,
|
url(r'^mark_all_as_read$', rest_dispatch,
|
||||||
{'POST': 'zerver.views.messages.mark_all_as_read'}),
|
{'POST': 'zerver.views.messages.mark_all_as_read'}),
|
||||||
|
|
Loading…
Reference in New Issue