Просмотр исходного кода

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 12 лет назад
Родитель
Сommit
b7a8bb9400

+ 4
- 0
oscar/apps/customer/app.py Просмотреть файл

30
     register_view = views.AccountRegistrationView
30
     register_view = views.AccountRegistrationView
31
     profile_view = views.ProfileView
31
     profile_view = views.ProfileView
32
     profile_update_view = views.ProfileUpdateView
32
     profile_update_view = views.ProfileUpdateView
33
+    profile_delete_view = views.ProfileDeleteView
33
     change_password_view = views.ChangePasswordView
34
     change_password_view = views.ChangePasswordView
34
 
35
 
35
     notification_inbox_view = notification_views.InboxView
36
     notification_inbox_view = notification_views.InboxView
70
             url(r'^profile/edit/$',
71
             url(r'^profile/edit/$',
71
                 login_required(self.profile_update_view.as_view()),
72
                 login_required(self.profile_update_view.as_view()),
72
                 name='profile-update'),
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
             # Order history
78
             # Order history
75
             url(r'^orders/$',
79
             url(r'^orders/$',

+ 20
- 0
oscar/apps/customer/forms.py Просмотреть файл

93
         return url
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
 class CommonPasswordValidator(validators.BaseValidator):
116
 class CommonPasswordValidator(validators.BaseValidator):
97
     # See
117
     # See
98
     # http://www.smartplanet.com/blog/business-brains/top-20-most-common-passwords-of-all-time-revealed-8216123456-8216princess-8216qwerty/4519  # noqa
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 Просмотреть файл

24
 EmailAuthenticationForm, EmailUserCreationForm, OrderSearchForm = get_classes(
24
 EmailAuthenticationForm, EmailUserCreationForm, OrderSearchForm = get_classes(
25
     'customer.forms', ['EmailAuthenticationForm', 'EmailUserCreationForm',
25
     'customer.forms', ['EmailAuthenticationForm', 'EmailUserCreationForm',
26
                        'OrderSearchForm'])
26
                        'OrderSearchForm'])
27
-ProfileForm = get_class('customer.forms', 'ProfileForm')
27
+ProfileForm, ConfirmPasswordForm = get_classes(
28
+    'customer.forms', ['ProfileForm', 'ConfirmPasswordForm'])
28
 UserAddressForm = get_class('address.forms', 'UserAddressForm')
29
 UserAddressForm = get_class('address.forms', 'UserAddressForm')
29
 user_registered = get_class('customer.signals', 'user_registered')
30
 user_registered = get_class('customer.signals', 'user_registered')
30
 Order = get_model('order', 'Order')
31
 Order = get_model('order', 'Order')
294
         return reverse('customer:profile-view')
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
 class ChangePasswordView(PageTitleMixin, FormView):
320
 class ChangePasswordView(PageTitleMixin, FormView):
298
     form_class = PasswordChangeForm
321
     form_class = PasswordChangeForm
299
     template_name = 'customer/profile/change_password_form.html'
322
     template_name = 'customer/profile/change_password_form.html'

+ 4
- 1
oscar/apps/order/abstract_models.py Просмотреть файл

127
 
127
 
128
     @property
128
     @property
129
     def is_anonymous(self):
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
     @property
135
     @property
133
     def basket_total_before_discounts_incl_tax(self):
136
     def basket_total_before_discounts_incl_tax(self):

+ 1
- 1
oscar/templates/oscar/customer/profile/profile.html Просмотреть файл

31
 
31
 
32
     <a href="{% url 'customer:change-password' %}" class="btn btn-primary">{% trans 'Change password' %}</a>
32
     <a href="{% url 'customer:change-password' %}" class="btn btn-primary">{% trans 'Change password' %}</a>
33
     <a href="{% url 'customer:profile-update' %}" class="btn btn-primary">{% trans 'Edit profile' %}</a>
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
 {% endblock %}
36
 {% endblock %}
36
-

+ 21
- 0
oscar/templates/oscar/customer/profile/profile_delete.html Просмотреть файл

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 Просмотреть файл

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

+ 4
- 2
oscar/templates/oscar/dashboard/orders/order_list.html Просмотреть файл

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

+ 23
- 2
tests/functional/customer/profile_tests.py Просмотреть файл

8
 from oscar.core.compat import get_user_model
8
 from oscar.core.compat import get_user_model
9
 from oscar.apps.basket.models import Basket
9
 from oscar.apps.basket.models import Basket
10
 from oscar.apps.partner import strategy
10
 from oscar.apps.partner import strategy
11
+from oscar.apps.order.models import Order
11
 
12
 
12
 
13
 
13
 User = get_user_model()
14
 User = get_user_model()
28
         self.assertEqual(200, response.status_code)
29
         self.assertEqual(200, response.status_code)
29
         self.assertTrue(self.email in response.content)
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
     def test_can_update_their_name(self):
52
     def test_can_update_their_name(self):
32
         profile_form_page = self.app.get(reverse('customer:profile-update'),
53
         profile_form_page = self.app.get(reverse('customer:profile-update'),
33
-                                user=self.user)
54
+                                         user=self.user)
34
         self.assertEqual(200, profile_form_page.status_code)
55
         self.assertEqual(200, profile_form_page.status_code)
35
         form = profile_form_page.forms['profile_form']
56
         form = profile_form_page.forms['profile_form']
36
         form['first_name'] = 'Barry'
57
         form['first_name'] = 'Barry'
40
 
61
 
41
     def test_can_update_their_email_address_and_name(self):
62
     def test_can_update_their_email_address_and_name(self):
42
         profile_form_page = self.app.get(reverse('customer:profile-update'),
63
         profile_form_page = self.app.get(reverse('customer:profile-update'),
43
-                                user=self.user)
64
+                                         user=self.user)
44
         self.assertEqual(200, profile_form_page.status_code)
65
         self.assertEqual(200, profile_form_page.status_code)
45
         form = profile_form_page.forms['profile_form']
66
         form = profile_form_page.forms['profile_form']
46
         form['email'] = 'new@example.com'
67
         form['email'] = 'new@example.com'

Загрузка…
Отмена
Сохранить