Explorar el Código

Allow customers to delete their profile

We add a new form to the profile section that requires the customer to
confirm their password, but which then deletes their user.
master
David Winterbottom hace 12 años
padre
commit
b7a8bb9400

+ 4
- 0
oscar/apps/customer/app.py Ver fichero

@@ -30,6 +30,7 @@ class CustomerApplication(Application):
30 30
     register_view = views.AccountRegistrationView
31 31
     profile_view = views.ProfileView
32 32
     profile_update_view = views.ProfileUpdateView
33
+    profile_delete_view = views.ProfileDeleteView
33 34
     change_password_view = views.ChangePasswordView
34 35
 
35 36
     notification_inbox_view = notification_views.InboxView
@@ -70,6 +71,9 @@ class CustomerApplication(Application):
70 71
             url(r'^profile/edit/$',
71 72
                 login_required(self.profile_update_view.as_view()),
72 73
                 name='profile-update'),
74
+            url(r'^profile/delete/$',
75
+                login_required(self.profile_delete_view.as_view()),
76
+                name='profile-delete'),
73 77
 
74 78
             # Order history
75 79
             url(r'^orders/$',

+ 20
- 0
oscar/apps/customer/forms.py Ver fichero

@@ -93,6 +93,26 @@ class EmailAuthenticationForm(AuthenticationForm):
93 93
         return url
94 94
 
95 95
 
96
+class ConfirmPasswordForm(forms.Form):
97
+    """
98
+    Extends the standard django AuthenticationForm, to support 75 character
99
+    usernames. 75 character usernames are needed to support the EmailOrUsername
100
+    auth backend.
101
+    """
102
+    password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
103
+
104
+    def __init__(self, user, *args, **kwargs):
105
+        super(ConfirmPasswordForm, self).__init__(*args, **kwargs)
106
+        self.user = user
107
+
108
+    def clean_password(self):
109
+        password = self.cleaned_data['password']
110
+        if not self.user.check_password(password):
111
+            raise forms.ValidationError(
112
+                _("The entered password is not valid!"))
113
+        return password
114
+
115
+
96 116
 class CommonPasswordValidator(validators.BaseValidator):
97 117
     # See
98 118
     # http://www.smartplanet.com/blog/business-brains/top-20-most-common-passwords-of-all-time-revealed-8216123456-8216princess-8216qwerty/4519  # noqa

+ 24
- 1
oscar/apps/customer/views.py Ver fichero

@@ -24,7 +24,8 @@ Dispatcher = get_class('customer.utils', 'Dispatcher')
24 24
 EmailAuthenticationForm, EmailUserCreationForm, OrderSearchForm = get_classes(
25 25
     'customer.forms', ['EmailAuthenticationForm', 'EmailUserCreationForm',
26 26
                        'OrderSearchForm'])
27
-ProfileForm = get_class('customer.forms', 'ProfileForm')
27
+ProfileForm, ConfirmPasswordForm = get_classes(
28
+    'customer.forms', ['ProfileForm', 'ConfirmPasswordForm'])
28 29
 UserAddressForm = get_class('address.forms', 'UserAddressForm')
29 30
 user_registered = get_class('customer.signals', 'user_registered')
30 31
 Order = get_model('order', 'Order')
@@ -294,6 +295,28 @@ class ProfileUpdateView(PageTitleMixin, FormView):
294 295
         return reverse('customer:profile-view')
295 296
 
296 297
 
298
+class ProfileDeleteView(PageTitleMixin, FormView):
299
+    form_class = ConfirmPasswordForm
300
+    template_name = 'customer/profile/profile_delete.html'
301
+    page_title = _('Delete profile')
302
+    active_tab = 'profile'
303
+
304
+    def get_form_kwargs(self):
305
+        kwargs = super(ProfileDeleteView, self).get_form_kwargs()
306
+        kwargs['user'] = self.request.user
307
+        return kwargs
308
+
309
+    def form_valid(self, form):
310
+        self.request.user.delete()
311
+        messages.success(
312
+            self.request,
313
+            _("Your profile has now been deleted. Thanks for using the site."))
314
+        return HttpResponseRedirect(self.get_success_url())
315
+
316
+    def get_success_url(self):
317
+        return reverse('promotions:home')
318
+
319
+
297 320
 class ChangePasswordView(PageTitleMixin, FormView):
298 321
     form_class = PasswordChangeForm
299 322
     template_name = 'customer/profile/change_password_form.html'

+ 4
- 1
oscar/apps/order/abstract_models.py Ver fichero

@@ -127,7 +127,10 @@ class AbstractOrder(models.Model):
127 127
 
128 128
     @property
129 129
     def is_anonymous(self):
130
-        return self.user is None
130
+        # It's possible for an order to be placed by a customer who then
131
+        # deletes their profile.  Hence, we need to check that a guest email is
132
+        # set.
133
+        return self.user is None and bool(self.guest_email)
131 134
 
132 135
     @property
133 136
     def basket_total_before_discounts_incl_tax(self):

+ 1
- 1
oscar/templates/oscar/customer/profile/profile.html Ver fichero

@@ -31,6 +31,6 @@
31 31
 
32 32
     <a href="{% url 'customer:change-password' %}" class="btn btn-primary">{% trans 'Change password' %}</a>
33 33
     <a href="{% url 'customer:profile-update' %}" class="btn btn-primary">{% trans 'Edit profile' %}</a>
34
+    <a id="delete_profile" href="{% url 'customer:profile-delete' %}" class="btn btn-danger">{% trans 'Delete profile' %}</a>
34 35
 
35 36
 {% endblock %}
36
-

+ 21
- 0
oscar/templates/oscar/customer/profile/profile_delete.html Ver fichero

@@ -0,0 +1,21 @@
1
+{% extends "customer/baseaccountpage.html" %}
2
+{% load i18n %}
3
+
4
+{% block tabcontent %}
5
+    <p>{% trans "Please confirm your password to delete your profile." %}</p>
6
+    <form id="delete_profile_form" action="." method="post" class="form-horizontal">
7
+        {% csrf_token %}
8
+        {% include "partials/form_fields.html" %}
9
+        <div class="alert alert-warning">
10
+            <h3>{% trans "Warning" %}</h3>
11
+            {% blocktrans %}
12
+                This will delete all information about you from the site.  Deleting your profile cannot be
13
+                undone.
14
+            {% endblocktrans %}
15
+        </div>
16
+        <div class="form-actions">
17
+            <button class="btn btn-large btn-danger" type="submit">{% trans "Delete" %}</button>
18
+            {% trans "or" %} <a href="{% url "customer:profile-view" %}">{% trans "cancel" %}</a>.
19
+        </div>
20
+    </form>
21
+{% endblock tabcontent %}

+ 4
- 2
oscar/templates/oscar/dashboard/orders/order_detail.html Ver fichero

@@ -30,7 +30,9 @@
30 30
 {% block dashboard_content %}
31 31
     <table class="table table-striped table-bordered table-hover">
32 32
         <caption><i class="icon-group icon-large"></i>{% trans "Customer Information" %}</caption>
33
-        {% if order.user %}
33
+        {% if order.guest_email %}
34
+            <tr><td>{% trans "Customer checked out as a guest." %}</td></tr>
35
+        {% elif order.user %}
34 36
             <tr>
35 37
                 <th>{% trans "Name" %}</th>
36 38
                 <th>{% trans "Email address" %}</th>
@@ -40,7 +42,7 @@
40 42
                 <td>{{ order.user.email|default:"-" }}</td>
41 43
             </tr>
42 44
         {% else %}
43
-            <tr><td>{% trans "Customer checked out anonymously." %}</td></tr>
45
+            <tr><td>{% trans "Customer has deleted their account." %}</td></tr>
44 46
         {% endif %}
45 47
     </table>
46 48
 

+ 4
- 2
oscar/templates/oscar/dashboard/orders/order_list.html Ver fichero

@@ -111,10 +111,12 @@
111 111
                             <td>{{ order.num_items }}</td>
112 112
                             <td>{{ order.status|default:"-" }}</td>
113 113
                             <td>
114
-                                {% if not order.is_anonymous %}
114
+                                {% if order.guest_email %}
115
+                                    {{ order.guest_email }}
116
+                                {% elif order.user %}
115 117
                                     <a href="{% url 'dashboard:user-detail' pk=order.user.id %}">{{ order.user.get_full_name|default:"-" }}</a>
116 118
                                 {% else %}
117
-                                    {{ order.email }}
119
+                                    &lt;{% trans "Deleted" %}&gt;
118 120
                                 {% endif %}
119 121
                             </td>
120 122
                             <td>{{ order.shipping_address|default:"-" }}</td>

+ 23
- 2
tests/functional/customer/profile_tests.py Ver fichero

@@ -8,6 +8,7 @@ from oscar.test.testcases import WebTestCase
8 8
 from oscar.core.compat import get_user_model
9 9
 from oscar.apps.basket.models import Basket
10 10
 from oscar.apps.partner import strategy
11
+from oscar.apps.order.models import Order
11 12
 
12 13
 
13 14
 User = get_user_model()
@@ -28,9 +29,29 @@ class TestASignedInUser(WebTestCase):
28 29
         self.assertEqual(200, response.status_code)
29 30
         self.assertTrue(self.email in response.content)
30 31
 
32
+    def test_can_delete_their_profile(self):
33
+        user_id = self.user.id
34
+        order_id = self.order.id
35
+
36
+        profile = self.app.get(reverse('customer:profile-view'),
37
+                               user=self.user)
38
+        delete_confirm = profile.click(linkid="delete_profile")
39
+        form = delete_confirm.forms['delete_profile_form']
40
+        form['password'] = self.password
41
+        form.submit()
42
+
43
+        # Ensure user is deleted
44
+        users = User.objects.filter(id=user_id)
45
+        self.assertEqual(0, len(users))
46
+
47
+        # Ensure order isn't deleted
48
+        users = User.objects.filter(id=user_id)
49
+        orders = Order.objects.filter(id=order_id)
50
+        self.assertEqual(1, len(orders))
51
+
31 52
     def test_can_update_their_name(self):
32 53
         profile_form_page = self.app.get(reverse('customer:profile-update'),
33
-                                user=self.user)
54
+                                         user=self.user)
34 55
         self.assertEqual(200, profile_form_page.status_code)
35 56
         form = profile_form_page.forms['profile_form']
36 57
         form['first_name'] = 'Barry'
@@ -40,7 +61,7 @@ class TestASignedInUser(WebTestCase):
40 61
 
41 62
     def test_can_update_their_email_address_and_name(self):
42 63
         profile_form_page = self.app.get(reverse('customer:profile-update'),
43
-                                user=self.user)
64
+                                         user=self.user)
44 65
         self.assertEqual(200, profile_form_page.status_code)
45 66
         form = profile_form_page.forms['profile_form']
46 67
         form['email'] = 'new@example.com'

Loading…
Cancelar
Guardar