Add UI for deactivating your own Zulip account.

Fixes #1009.
This commit is contained in:
Amy Liu
2016-10-13 14:09:32 -04:00
committed by Tim Abbott
parent 3d183ffe23
commit 3ee777a11a
5 changed files with 80 additions and 1 deletions

View File

@@ -452,6 +452,25 @@ function _setup_page() {
$('#default_language_modal').modal('show'); $('#default_language_modal').modal('show');
}); });
$("#user_deactivate_account_button").on('click', function (e) {
e.preventDefault();
e.stopPropagation();
$("#deactivate_self_modal").modal("show");
});
$("#do_deactivate_self_button").on('click',function (e) {
$("#deactivate_self_modal").modal("hide");
channel.del({
url: '/json/users/me',
success: function () {
window.location.href = "/login";
},
error: function (xhr, error_type) {
ui.report_error(i18n.t("Error deactivating account"), xhr, $('#settings-status').expectOne());
}
});
});
$("#get_api_key_box").hide(); $("#get_api_key_box").hide();
$("#show_api_key_box").hide(); $("#show_api_key_box").hide();
$("#get_api_key_box form").ajaxForm({ $("#get_api_key_box form").ajaxForm({

View File

@@ -56,9 +56,26 @@
<input type="file" name="user_avatar_file_input" class="notvisible" id="user_avatar_file_input" value="{{t 'Upload avatar' }}" /> <input type="file" name="user_avatar_file_input" class="notvisible" id="user_avatar_file_input" value="{{t 'Upload avatar' }}" />
<div id="upload_avatar_spinner"></div> <div id="upload_avatar_spinner"></div>
<button class="button standalone" id="user_avatar_upload_button">{{t 'Customize avatar' }}</button> <button class="button standalone" id="user_avatar_upload_button">{{t 'Customize avatar' }}</button>
<button class="button standalone btn-danger" id="user_deactivate_account_button">{{t 'Deactivate Account' }}</button>
</div> </div>
</form> </form>
<div id="deactivate_self_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="deactivation_self_modal_label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="deactivation_user_modal_label">{{t "Deactivate Your Account" }} </h3>
</div>
<div class="modal-body">
<p>{{#tr this}}By deactivating your account, you will be logged out immediately.{{/tr}}</p>
<p>{{t "Note that any bots that you maintain will be disabled." }}</p>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">{{t "Cancel" }}</button>
<button class="button btn-danger" id="do_deactivate_self_button">{{t "Deactivate now" }}</button>
</div>
</div>
</div> </div>
</div> </div>

View File

@@ -16,6 +16,7 @@ from zerver.models import (
from zerver.lib.actions import ( from zerver.lib.actions import (
create_stream_if_needed, create_stream_if_needed,
set_default_streams, set_default_streams,
do_change_is_admin
) )
from zerver.lib.initial_password import initial_password from zerver.lib.initial_password import initial_password
@@ -749,3 +750,35 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(realm.name, realm_name) self.assertEqual(realm.name, realm_name)
self.assertEqual(realm.subdomain, subdomain) self.assertEqual(realm.subdomain, subdomain)
self.assertEqual(get_user_profile_by_email(email).realm, realm) self.assertEqual(get_user_profile_by_email(email).realm, realm)
class DeactivateUserTest(ZulipTestCase):
def test_deactivate_user(self):
# type: () -> None
email = 'hamlet@zulip.com'
self.login(email)
user = get_user_profile_by_email('hamlet@zulip.com')
self.assertTrue(user.is_active)
result = self.client_delete('/json/users/me')
self.assert_json_success(result)
user = get_user_profile_by_email('hamlet@zulip.com')
self.assertFalse(user.is_active)
self.login(email, fails=True)
def test_do_not_deactivate_final_admin(self):
# type: () -> None
email = 'iago@zulip.com'
self.login(email)
user = get_user_profile_by_email('iago@zulip.com')
self.assertTrue(user.is_active)
self.client_delete('/json/users/me')
user = get_user_profile_by_email('iago@zulip.com')
self.assertTrue(user.is_active)
self.assertTrue(user.is_realm_admin)
email = 'hamlet@zulip.com'
user_2 = get_user_profile_by_email('hamlet@zulip.com')
do_change_is_admin(user_2, True)
self.assertTrue(user_2.is_realm_admin)
result = self.client_delete('/json/users/me')
self.assert_json_success(result)
do_change_is_admin(user, True)

View File

@@ -33,6 +33,15 @@ def deactivate_user_backend(request, user_profile, email):
return json_error(_('No such user')) return json_error(_('No such user'))
return _deactivate_user_profile_backend(request, user_profile, target) return _deactivate_user_profile_backend(request, user_profile, target)
def deactivate_user_own_backend(request, user_profile):
# type: (HttpRequest, UserProfile) -> HttpResponse
admins = set(user_profile.realm.get_admin_users())
if user_profile.is_realm_admin and len(admins) == 1:
return json_error(_('Cannot deactivate the only admin'))
do_deactivate_user(user_profile)
return json_success({})
def deactivate_bot_backend(request, user_profile, email): def deactivate_bot_backend(request, user_profile, email):
# type: (HttpRequest, UserProfile, text_type) -> HttpResponse # type: (HttpRequest, UserProfile, text_type) -> HttpResponse
try: try:

View File

@@ -175,7 +175,8 @@ v1_api_and_json_patterns = [
# users/me -> zerver.views # users/me -> zerver.views
url(r'^users/me$', 'zerver.lib.rest.rest_dispatch', url(r'^users/me$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'zerver.views.pointer.get_profile_backend'}), {'GET': 'zerver.views.pointer.get_profile_backend',
'DELETE':'zerver.views.users.deactivate_user_own_backend'}),
url(r'^users/me/pointer$', 'zerver.lib.rest.rest_dispatch', url(r'^users/me/pointer$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'zerver.views.pointer.get_pointer_backend', {'GET': 'zerver.views.pointer.get_pointer_backend',
'PUT': 'zerver.views.pointer.update_pointer_backend'}), 'PUT': 'zerver.views.pointer.update_pointer_backend'}),