Browse Source

"Close Account" section in Settings page. Fixes #95

pull/114/merge
Pēteris Caune 8 years ago
parent
commit
e685154cc2
8 changed files with 140 additions and 7 deletions
  1. +57
    -0
      hc/accounts/tests/test_close_account.py
  2. +1
    -0
      hc/accounts/urls.py
  3. +23
    -0
      hc/accounts/views.py
  4. +8
    -0
      hc/payments/models.py
  5. +1
    -1
      hc/payments/tests/test_cancel_plan.py
  6. +1
    -6
      hc/payments/views.py
  7. +6
    -0
      static/css/settings.css
  8. +43
    -0
      templates/accounts/profile.html

+ 57
- 0
hc/accounts/tests/test_close_account.py View File

@ -0,0 +1,57 @@
from django.contrib.auth.models import User
from mock import patch
from hc.test import BaseTestCase
from hc.api.models import Check
from hc.payments.models import Subscription
class CloseAccountTestCase(BaseTestCase):
@patch("hc.payments.models.Subscription.cancel")
def test_it_works(self, mock_cancel):
Check.objects.create(user=self.alice, tags="foo a-B_1 baz@")
Subscription.objects.create(user=self.alice, subscription_id="123")
self.client.login(username="[email protected]", password="password")
r = self.client.post("/accounts/close/")
self.assertEqual(r.status_code, 302)
# Alice should be gone
alices = User.objects.filter(username="alice")
self.assertFalse(alices.exists())
# Alice should be gone
alices = User.objects.filter(username="alice")
self.assertFalse(alices.exists())
# Bob's current team should be updated to self
self.bobs_profile.refresh_from_db()
self.assertEqual(self.bobs_profile.current_team, self.bobs_profile)
# Check should be gone
self.assertFalse(Check.objects.exists())
# Subscription should have been canceled
self.assertTrue(mock_cancel.called)
# Subscription should be gone
self.assertFalse(Subscription.objects.exists())
def test_partner_removal_works(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post("/accounts/close/")
self.assertEqual(r.status_code, 302)
# Alice should be still present
self.alice.refresh_from_db()
self.profile.refresh_from_db()
# Bob should be gone
bobs = User.objects.filter(username="bob")
self.assertFalse(bobs.exists())
def test_it_rejects_get(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/accounts/close/")
self.assertEqual(r.status_code, 405)

+ 1
- 0
hc/accounts/urls.py View File

@ -16,6 +16,7 @@ urlpatterns = [
url(r'^profile/$', views.profile, name="hc-profile"),
url(r'^profile/notifications/$', views.notifications, name="hc-notifications"),
url(r'^profile/badges/$', views.badges, name="hc-badges"),
url(r'^close/$', views.close, name="hc-close"),
url(r'^unsubscribe_reports/([\w-]+)/$',
views.unsubscribe_reports, name="hc-unsubscribe-reports"),


+ 23
- 0
hc/accounts/views.py View File

@ -12,12 +12,14 @@ from django.contrib.auth.models import User
from django.core import signing
from django.http import HttpResponseForbidden, HttpResponseBadRequest
from django.shortcuts import redirect, render
from django.views.decorators.http import require_POST
from hc.accounts.forms import (EmailPasswordForm, InviteTeamMemberForm,
RemoveTeamMemberForm, ReportSettingsForm,
SetPasswordForm, TeamNameForm)
from hc.accounts.models import Profile, Member
from hc.api.models import Channel, Check
from hc.lib.badges import get_badge_url
from hc.payments.models import Subscription
def _make_user(email):
@ -338,3 +340,24 @@ def switch_team(request, target_username):
request.user.profile.save()
return redirect("hc-checks")
@require_POST
@login_required
def close(request):
user = request.user
# Subscription needs to be canceled before it is deleted:
sub = Subscription.objects.filter(user=user).first()
if sub:
sub.cancel()
# Any users currently using this team need to switch to their own team:
for partner in Profile.objects.filter(current_team=user.profile):
partner.current_team = partner
partner.save()
user.delete()
request.session.flush()
return redirect("hc-index")

+ 8
- 0
hc/payments/models.py View File

@ -39,6 +39,14 @@ class Subscription(models.Model):
self._pm = braintree.PaymentMethod.find(self.payment_method_token)
return self._pm
def cancel(self):
if self.subscription_id:
braintree.Subscription.cancel(self.subscription_id)
self.subscription_id = ""
self.plan_id = ""
self.save()
def pm_is_credit_card(self):
return isinstance(self._get_braintree_payment_method(),
braintree.credit_card.CreditCard)


+ 1
- 1
hc/payments/tests/test_cancel_plan.py View File

@ -13,7 +13,7 @@ class CancelPlanTestCase(BaseTestCase):
self.sub.plan_id = "P5"
self.sub.save()
@patch("hc.payments.views.braintree")
@patch("hc.payments.models.braintree")
def test_it_works(self, mock_braintree):
self.client.login(username="[email protected]", password="password")


+ 1
- 6
hc/payments/views.py View File

@ -156,12 +156,7 @@ def update_payment_method(request):
@require_POST
def cancel_plan(request):
sub = Subscription.objects.get(user=request.user)
braintree.Subscription.cancel(sub.subscription_id)
sub.subscription_id = ""
sub.plan_id = ""
sub.save()
sub.cancel()
return redirect("hc-pricing")


+ 6
- 0
static/css/settings.css View File

@ -17,4 +17,10 @@
.page-profile .icon-ok {
color: #5cb85c;
}
#close-account {
margin-left: 24px;
border-color: #d43f3a;
color: #d43f3a;
}

+ 43
- 0
templates/accounts/profile.html View File

@ -136,6 +136,22 @@
{% endif %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-body settings-block">
{% csrf_token %}
<h2>Close Account</h2>
<a
id="close-account"
href="#"
class="btn btn-default pull-right"
data-toggle="modal"
data-target="#close-account-modal">Close Account</a>
This will permanently remove your healthchecks.io account
<form action="{% url 'hc-close' %}" method="post">
</form>
</div>
</div>
</div>
</div>
@ -268,6 +284,33 @@
</form>
</div>
</div>
<div id="close-account-modal" class="modal">
<div class="modal-dialog">
<form id="close-account-form" method="post" action="{% url 'hc-close' %}">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="remove-check-title">Close Account</h4>
</div>
<div class="modal-body">
<p></p>
<p>You are about to permanently remove
the account <strong>{{ profile }}</strong> and all
of its associated checks and integrations.</p>
<p>Are you sure?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button
type="submit"
class="btn btn-danger">Close Account</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block scripts %}


Loading…
Cancel
Save