org settings: Add button to deactivate organization.

This adds button under "Organization profile" settings, which
deactivates the organization and sends an "event" to all the
active user and log out them.

Fixes: #8212.
This commit is contained in:
Shubham Dhama 2018-01-30 19:28:50 +05:30 committed by Tim Abbott
parent 2733c0551e
commit 9feae472f8
9 changed files with 88 additions and 1 deletions

View File

@ -103,6 +103,8 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
page_params.realm_icon_url = event.data.icon_url; page_params.realm_icon_url = event.data.icon_url;
page_params.realm_icon_source = event.data.icon_source; page_params.realm_icon_source = event.data.icon_source;
realm_icon.rerender(); realm_icon.rerender();
} else if (event.op === 'deactivated') {
window.location.href = "/accounts/deactivated/";
} }
break; break;

View File

@ -871,6 +871,28 @@ function _set_up() {
} }
realm_icon.build_realm_icon_widget(upload_realm_icon); realm_icon.build_realm_icon_widget(upload_realm_icon);
$('#deactivate_realm_button').on('click', function (e) {
if (!overlays.is_modal_open()) {
e.preventDefault();
e.stopPropagation();
overlays.open_modal('deactivate-realm-modal');
}
});
$('#do_deactivate_realm_button').on('click', function () {
if (overlays.is_modal_open()) {
overlays.close_modal('deactivate-realm-modal');
}
channel.post({
url:'/json/realm/deactivate',
error: function (xhr) {
ui_report.error(
i18n.t("Failed"), xhr, $('#admin-realm-deactivation-status').expectOne()
);
},
});
});
} }
exports.set_up = function () { exports.set_up = function () {
i18n.ensure_i18n(_set_up); i18n.ensure_i18n(_set_up);

View File

@ -1142,7 +1142,8 @@ input[type=checkbox].inline-block {
margin-bottom: 5px; margin-bottom: 5px;
} }
#deactivate_self_modal { #deactivate_self_modal,
#deactivate-realm-modal {
box-shadow: 0px 0px 75px hsla(0, 0%, 0%, 0.5); box-shadow: 0px 0px 75px hsla(0, 0%, 0%, 0.5);
outline: 10000px solid hsla(0, 0%, 0%, 0.3); outline: 10000px solid hsla(0, 0%, 0%, 0.3);
border: none; border: none;

View File

@ -0,0 +1,13 @@
<div id="deactivate-realm-modal" class="modal modal-bg hide fade" tabindex="-1" role="dialog" aria-labelledby="deactivate_realm_modal_label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="{{t 'Close' }}"><span aria-hidden="true">&times;</span></button>
<h3 id="deactivate_realm_modal_label">{{t "Deactivate organization" }} <span class="realm_name"></span></h3>
</div>
<div class="modal-body">
<p>{{t "This action is permanent and cannot be undone. All users will permanently lose access to their Zulip accounts." }}</p>
</div>
<div class="modal-footer">
<button type="button" class="button rounded" data-dismiss="modal">{{t "Cancel" }}</button>
<button type="button" class="button rounded btn-danger" id="do_deactivate_realm_button">{{t "Deactivate organization" }}</button>
</div>
</div>

View File

@ -4,6 +4,7 @@
<div class="alert" id="admin-realm-name-status"></div> <div class="alert" id="admin-realm-name-status"></div>
<div class="alert" id="admin-realm-description-status"></div> <div class="alert" id="admin-realm-description-status"></div>
<div class="alert" id="admin-realm-deactivation-status"></div>
<div class="inline-block organization-settings-parent"> <div class="inline-block organization-settings-parent">
<div class="input-group admin-realm"> <div class="input-group admin-realm">
@ -43,5 +44,15 @@
id="realm_icon_delete_button">{{t 'Delete icon' }}</button> id="realm_icon_delete_button">{{t 'Delete icon' }}</button>
</div> </div>
</div> </div>
<h3 class="light">{{t "Deactivate organization" }}</h3>
<div class="deactivate-realm-section">
<div class="input-group">
<button class="button rounded btn-danger" id="deactivate_realm_button">
{{t 'Deactivate organization' }}
</button>
</div>
{{ partial "deactivate-realm-modal"}}
</div>
</form> </form>
</div> </div>

View File

@ -607,6 +607,10 @@ def do_deactivate_realm(realm: Realm) -> None:
# notice when they try to log in. # notice when they try to log in.
delete_user_sessions(user) delete_user_sessions(user)
event = dict(type="realm", op="deactivated",
realm_id=realm.id)
send_event(event, active_user_ids(realm.id))
def do_reactivate_realm(realm: Realm) -> None: def do_reactivate_realm(realm: Realm) -> None:
realm.deactivated = False realm.deactivated = False
realm.save(update_fields=["deactivated"]) realm.save(update_fields=["deactivated"])

View File

@ -267,6 +267,28 @@ class RealmTest(ZulipTestCase):
realm = get_realm('zulip') realm = get_realm('zulip')
self.assertNotEqual(realm.default_language, invalid_lang) self.assertNotEqual(realm.default_language, invalid_lang)
def test_deactivate_realm_by_admin(self) -> None:
email = self.example_email('iago')
self.login(email)
realm = get_realm('zulip')
self.assertFalse(realm.deactivated)
result = self.client_post('/json/realm/deactivate')
self.assert_json_success(result)
realm = get_realm('zulip')
self.assertTrue(realm.deactivated)
def test_deactivate_realm_by_non_admin(self) -> None:
email = self.example_email('hamlet')
self.login(email)
realm = get_realm('zulip')
self.assertFalse(realm.deactivated)
result = self.client_post('/json/realm/deactivate')
self.assert_json_error(result, "Must be a realm administrator")
realm = get_realm('zulip')
self.assertFalse(realm.deactivated)
class RealmAPITest(ZulipTestCase): class RealmAPITest(ZulipTestCase):

View File

@ -10,6 +10,7 @@ from zerver.lib.actions import (
do_set_realm_notifications_stream, do_set_realm_notifications_stream,
do_set_realm_signup_notifications_stream, do_set_realm_signup_notifications_stream,
do_set_realm_property, do_set_realm_property,
do_deactivate_realm,
) )
from zerver.lib.i18n import get_available_language_codes from zerver.lib.i18n import get_available_language_codes
from zerver.lib.request import has_request_variables, REQ, JsonableError from zerver.lib.request import has_request_variables, REQ, JsonableError
@ -124,3 +125,10 @@ def update_realm(
data['signup_notifications_stream_id'] = signup_notifications_stream_id data['signup_notifications_stream_id'] = signup_notifications_stream_id
return json_success(data) return json_success(data)
@require_realm_admin
@has_request_variables
def deactivate_realm(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
realm = user_profile.realm
do_deactivate_realm(realm)
return json_success()

View File

@ -106,6 +106,10 @@ v1_api_and_json_patterns = [
{'PATCH': 'zerver.views.custom_profile_fields.update_realm_custom_profile_field', {'PATCH': 'zerver.views.custom_profile_fields.update_realm_custom_profile_field',
'DELETE': 'zerver.views.custom_profile_fields.delete_realm_custom_profile_field'}), 'DELETE': 'zerver.views.custom_profile_fields.delete_realm_custom_profile_field'}),
# realm/deactivate -> zerver.views.deactivate_realm
url(r'^realm/deactivate$', rest_dispatch,
{'POST': 'zerver.views.realm.deactivate_realm'}),
# users -> zerver.views.users # users -> zerver.views.users
# #
# Since some of these endpoints do something different if used on # Since some of these endpoints do something different if used on